amosnbn commited on
Commit
6c56332
·
1 Parent(s): 5b5694b

Fix responsive layout

Browse files
Files changed (2) hide show
  1. frontend/about.html +166 -56
  2. frontend/index.html +121 -34
frontend/about.html CHANGED
@@ -8,49 +8,99 @@
8
  :root{
9
  --bg:#f6f7f9; --card:#fff; --ink:#111; --sub:#666; --line:#e9ecef;
10
  --shadow:0 8px 24px rgba(0,0,0,.08); --radius:14px; --pad:clamp(12px,2.5vw,20px); --maxw:1120px;
 
11
  }
12
  *{box-sizing:border-box}
13
- html,body{margin:0;padding:0;font-family:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Noto Sans,Arial,sans-serif;color:var(--ink);background:var(--bg);line-height:1.6}
14
 
15
- header{position:sticky;top:0;z-index:30;background:var(--card);box-shadow:0 4px 16px rgba(0,0,0,.06)}
16
- .nav{max-width:var(--maxw);margin:auto;display:flex;align-items:center;justify-content:space-between;padding:var(--pad)}
17
- .logo{font-weight:800;font-size:clamp(18px,4vw,22px)}
18
- .menu{display:flex;gap:8px;flex-wrap:wrap}
19
- .pill{display:inline-flex;align-items:center;justify-content:center;padding:10px 14px;border:1px solid var(--ink);border-radius:999px;min-height:42px}
20
- .pill:hover{background:var(--ink);color:#fff}
 
 
 
21
 
 
 
 
 
 
22
  .hero{background:var(--card);border-bottom:1px solid var(--line)}
23
  .hero-inner{max-width:var(--maxw);margin:auto;text-align:center;padding:clamp(18px,6vw,30px) var(--pad)}
24
- .hero h2{margin:0 0 6px;font-size:clamp(20px,6vw,28px)}
25
- .hero p{margin:0;color:var(--sub)}
26
 
 
27
  .wrap{max-width:var(--maxw);margin:auto;padding:var(--pad)}
28
  .card{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow);padding:var(--pad);margin-bottom:clamp(12px,2vw,16px)}
29
- .title{font-weight:800;margin:0 0 8px;font-size:clamp(16px,4.5vw,18px)}
30
- .meta{display:grid;grid-template-columns:minmax(120px,160px) 1fr;gap:8px;margin-top:8px}
31
- .label{color:#666}
32
- .value{color:#111}
33
-
34
- .team{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(12px,2.4vw,18px)}
35
- .member{background:var(--card);border:1px solid var(--line);border-radius:12px;box-shadow:0 4px 14px rgba(0,0,0,.06);padding:clamp(12px,2.2vw,18px);text-align:center}
36
- .avatar{width:clamp(120px,28vw,160px);height:clamp(120px,28vw,160px);border-radius:50%;margin-inline:auto 10px auto;display:flex;align-items:center;justify-content:center;overflow:hidden;border:2px solid #ddd;cursor:pointer;transition:transform .2s}
37
- .avatar:hover{transform:scale(1.03)}
 
 
 
 
38
  .avatar img{width:100%;height:100%;object-fit:cover}
39
- .m-name{font-weight:800;margin-top:8px}
40
- .m-role{color:#666;font-size:clamp(12px,3.3vw,14px)}
41
-
42
- /* Modal */
43
- .modal{display:none;position:fixed;inset:0;z-index:1000;background:rgba(0,0,0,.8)}
44
- .modal img{display:block;margin:auto;max-width:86vw;max-height:86vh;border-radius:10px;box-shadow:0 8px 30px rgba(0,0,0,.35)}
45
- .modal .caption{color:#fff;text-align:center;padding:8px;font-size:clamp(12px,3.2vw,16px)}
46
- .close{position:absolute;top:14px;right:18px;color:#fff;font-size:34px;font-weight:800;cursor:pointer}
47
-
48
- @media (max-width:900px){ .team{grid-template-columns:repeat(2,1fr)} }
49
- @media (max-width:600px){
50
- .meta{grid-template-columns:1fr}
51
- .team{grid-template-columns:1fr}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
  body{padding-bottom:env(safe-area-inset-bottom)}
 
54
  </style>
55
  </head>
56
  <body>
@@ -58,6 +108,13 @@
58
  <header>
59
  <div class="nav" role="navigation" aria-label="Menu about">
60
  <div class="logo">PapuaTranslate</div>
 
 
 
 
 
 
 
61
  <nav class="menu">
62
  <a class="pill" href="/">Beranda</a>
63
  <a class="pill" href="/about">About</a>
@@ -77,11 +134,18 @@
77
  <section class="card">
78
  <h3 class="title">Judul Skripsi</h3>
79
  <p><b>Penerapan Causal Encoder Decoder Natural Language Instructions in Indonesian (CENDOL) untuk Translasi Bahasa Papua ke Bahasa Indonesia Baku</b></p>
80
- <div class="meta" style="margin-top:10px">
81
- <div class="label">Dosen Pembimbing</div><div class="value">Viny Christanti Mawardi, S.Kom., M.Kom.</div>
82
- <div class="label">Dosen Pendamping</div><div class="value">Manatap Dolok Lauro, S.Kom., M.M.S.I.</div>
83
- <div class="label">Program/Kampus</div><div class="value">Teknik Informatika, Universitas Tarumanagara</div>
84
- <div class="label">Tahun</div><div class="value">2025</div>
 
 
 
 
 
 
 
85
  </div>
86
  </section>
87
 
@@ -90,21 +154,23 @@
90
  <div class="team">
91
  <div class="member">
92
  <div class="avatar" onclick="openModal('m1')">
93
- <img src="{{ url_for('static', filename='image/pembuat.jpg') }}" alt="Amos Valentino Nababan" onerror="this.style.display='none'; this.parentNode.innerHTML='<div style=&quot;color:#999&quot;>Foto Tidak Ditemukan</div>'"/>
94
  </div>
95
  <div class="m-name">Amos Valentino Nababan</div>
96
  <div class="m-role">Pembuat</div>
97
  </div>
 
98
  <div class="member">
99
  <div class="avatar" onclick="openModal('m2')">
100
- <img src="{{ url_for('static', filename='image/dosen1.jpg') }}" alt="Viny Christanti Mawardi" onerror="this.style.display='none'; this.parentNode.innerHTML='<div style=&quot;color:#999&quot;>Foto Tidak Ditemukan</div>'"/>
101
  </div>
102
  <div class="m-name">Viny Christanti Mawardi, S.Kom., M.Kom.</div>
103
  <div class="m-role">Dosen Pembimbing</div>
104
  </div>
 
105
  <div class="member">
106
  <div class="avatar" onclick="openModal('m3')">
107
- <img src="{{ url_for('static', filename='image/dosen2.jpg') }}" alt="Manatap Dolok Lauro" onerror="this.style.display='none'; this.parentNode.innerHTML='<div style=&quot;color:#999&quot;>Foto Tidak Ditemukan</div>'"/>
108
  </div>
109
  <div class="m-name">Manatap Dolok Lauro, S.Kom., M.M.S.I.</div>
110
  <div class="m-role">Dosen Pendamping</div>
@@ -114,38 +180,82 @@
114
 
115
  <section class="card">
116
  <h3 class="title">Deskripsi Singkat</h3>
117
- <p>Aplikasi ini menerapkan arsitektur <i>Causal Encoder-Decoder</i> (CENDOL) berbasis mT5 dengan LoRA Adapter untuk translasi dialek Papua ke Bahasa Indonesia baku.</p>
118
  </section>
119
  </main>
120
 
121
  <footer>
122
- <div class="wrap" style="text-align:center;padding-top:8px;border-top:1px solid var(--line)">
123
- © 2025 PapuaTranslate • Halaman About
124
  </div>
125
  </footer>
126
 
127
  <!-- Modals -->
128
  <div id="m1" class="modal" role="dialog" aria-modal="true" aria-label="Foto Amos">
129
- <span class="close" onclick="closeModal('m1')">×</span>
130
- <img src="{{ url_for('static', filename='image/pembuat.jpg') }}" alt="Amos Valentino Nababan">
131
- <div class="caption">Amos Valentino Nababan — Pembuat</div>
 
 
132
  </div>
 
133
  <div id="m2" class="modal" role="dialog" aria-modal="true" aria-label="Foto Viny">
134
- <span class="close" onclick="closeModal('m2')">×</span>
135
- <img src="{{ url_for('static', filename='image/dosen1.jpg') }}" alt="Viny Christanti Mawardi">
136
- <div class="caption">Viny Christanti Mawardi, S.Kom., M.Kom.</div>
 
 
137
  </div>
 
138
  <div id="m3" class="modal" role="dialog" aria-modal="true" aria-label="Foto Manatap">
139
- <span class="close" onclick="closeModal('m3')">×</span>
140
- <img src="{{ url_for('static', filename='image/dosen2.jpg') }}" alt="Manatap Dolok Lauro">
141
- <div class="caption">Manatap Dolok Lauro, S.Kom., M.M.S.I.</div>
 
 
142
  </div>
143
 
144
  <script>
145
- function openModal(id){ document.getElementById(id).style.display = 'block'; }
146
- function closeModal(id){ document.getElementById(id).style.display = 'none'; }
147
- window.addEventListener('keydown', e => { if (e.key === 'Escape') ['m1','m2','m3'].forEach(closeModal) });
148
- window.addEventListener('click', e => { if (e.target.classList.contains('modal')) e.target.style.display='none'; });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
  </script>
150
  </body>
151
- </html>
 
8
  :root{
9
  --bg:#f6f7f9; --card:#fff; --ink:#111; --sub:#666; --line:#e9ecef;
10
  --shadow:0 8px 24px rgba(0,0,0,.08); --radius:14px; --pad:clamp(12px,2.5vw,20px); --maxw:1120px;
11
+ --fx:clamp(14px,2.6vw,16px);
12
  }
13
  *{box-sizing:border-box}
14
+ html,body{margin:0;padding:0;font-family:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Noto Sans,Arial,sans-serif;color:var(--ink);background:var(--bg);line-height:1.6;font-size:var(--fx)}
15
 
16
+ /* Header Responsif */
17
+ header{position:sticky;top:0;z-index:30;background:var(--card);box-shadow:0 4px 16px rgba(0,0,0,.06);backdrop-filter:blur(8px)}
18
+ .nav{max-width:var(--maxw);margin:auto;display:flex;align-items:center;justify-content:space-between;padding:var(--pad);gap:10px}
19
+ .logo{font-weight:800;font-size:clamp(18px,4vw,22px);white-space:nowrap}
20
+
21
+ /* Menu Responsif */
22
+ .menu{display:flex;gap:6px;flex-wrap:wrap;justify-content:flex-end}
23
+ .pill{display:inline-flex;align-items:center;justify-content:center;padding:8px 12px;border:1px solid var(--ink);border-radius:999px;min-height:38px;font-size:clamp(12px,3vw,14px);white-space:nowrap;transition:all 0.2s ease}
24
+ .pill:hover{background:var(--ink);color:#fff;transform:translateY(-1px)}
25
 
26
+ /* Mobile Menu */
27
+ .menu-toggle{display:none;flex-direction:column;gap:3px;background:none;border:none;cursor:pointer;padding:8px}
28
+ .menu-toggle span{width:20px;height:2px;background:var(--ink);transition:0.3s}
29
+
30
+ /* Hero Responsif */
31
  .hero{background:var(--card);border-bottom:1px solid var(--line)}
32
  .hero-inner{max-width:var(--maxw);margin:auto;text-align:center;padding:clamp(18px,6vw,30px) var(--pad)}
33
+ .hero h2{margin:0 0 6px;font-size:clamp(20px,6vw,28px);line-height:1.3}
34
+ .hero p{margin:0;color:var(--sub);font-size:clamp(13px,3.5vw,15px);max-width:600px;margin-inline:auto}
35
 
36
+ /* Main Content */
37
  .wrap{max-width:var(--maxw);margin:auto;padding:var(--pad)}
38
  .card{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow);padding:var(--pad);margin-bottom:clamp(12px,2vw,16px)}
39
+ .title{font-weight:800;margin:0 0 12px;font-size:clamp(16px,4.5vw,18px);line-height:1.3}
40
+
41
+ /* Meta Info Responsif */
42
+ .meta{display:grid;grid-template-columns:minmax(140px,160px) 1fr;gap:12px;margin-top:12px}
43
+ .label{color:var(--sub);font-weight:600;font-size:clamp(13px,3.5vw,15px)}
44
+ .value{color:var(--ink);font-size:clamp(13px,3.5vw,15px);line-height:1.4}
45
+
46
+ /* Team Grid Responsif */
47
+ .team{display:grid;grid-template-columns:repeat(auto-fit, minmax(280px, 1fr));gap:clamp(16px,3vw,24px)}
48
+ .member{background:var(--card);border:1px solid var(--line);border-radius:16px;box-shadow:var(--shadow);padding:clamp(16px,3vw,24px);text-align:center;transition:transform 0.2s ease}
49
+ .member:hover{transform:translateY(-2px)}
50
+ .avatar{width:clamp(100px,20vw,140px);height:clamp(100px,20vw,140px);border-radius:50%;margin:0 auto 16px;display:flex;align-items:center;justify-content:center;overflow:hidden;border:3px solid #f0f0f0;cursor:pointer;transition:all 0.3s ease}
51
+ .avatar:hover{transform:scale(1.05);border-color:#ddd}
52
  .avatar img{width:100%;height:100%;object-fit:cover}
53
+ .m-name{font-weight:800;margin:0 0 4px;font-size:clamp(14px,3.8vw,16px);line-height:1.3}
54
+ .m-role{color:var(--sub);font-size:clamp(12px,3.3vw,14px);line-height:1.4}
55
+
56
+ /* Modal Responsif */
57
+ .modal{display:none;position:fixed;inset:0;z-index:1000;background:rgba(0,0,0,.9);padding:20px;align-items:center;justify-content:center}
58
+ .modal-content{position:relative;max-width:90vw;max-height:90vh}
59
+ .modal img{display:block;max-width:100%;max-height:70vh;border-radius:12px;box-shadow:0 8px 30px rgba(0,0,0,.5);margin:0 auto}
60
+ .modal .caption{color:#fff;text-align:center;padding:16px 0;font-size:clamp(14px,3.8vw,18px);font-weight:600}
61
+ .close{position:absolute;top:-40px;right:0;color:#fff;font-size:32px;font-weight:700;cursor:pointer;width:40px;height:40px;display:flex;align-items:center;justify-content:center}
62
+
63
+ /* Mobile Styles */
64
+ @media (max-width: 768px) {
65
+ .nav{flex-wrap:wrap;gap:12px}
66
+ .menu-toggle{display:flex}
67
+ .menu{display:none;width:100%;flex-direction:column;gap:8px}
68
+ .menu.active{display:flex}
69
+ .pill{justify-content:center;min-height:44px;font-size:14px}
70
+
71
+ .hero-inner{padding:clamp(16px,5vw,24px) var(--pad)}
72
+ .hero h2{font-size:clamp(18px,5vw,24px)}
73
+
74
+ .meta{grid-template-columns:1fr;gap:8px}
75
+ .label{font-weight:700}
76
+
77
+ .team{grid-template-columns:1fr;gap:16px}
78
+ .member{padding:20px}
79
+ .avatar{width:120px;height:120px}
80
  }
81
+
82
+ @media (max-width: 480px) {
83
+ .nav{padding:12px}
84
+ .logo{font-size:16px}
85
+ .pill{padding:10px 16px;font-size:13px}
86
+
87
+ .hero h2{font-size:20px}
88
+ .hero p{font-size:13px}
89
+
90
+ .card{padding:16px;margin-bottom:12px}
91
+ .title{font-size:16px}
92
+
93
+ .m-name{font-size:15px}
94
+ .m-role{font-size:13px}
95
+
96
+ .modal{padding:10px}
97
+ .modal img{max-width:95vw;max-height:60vh}
98
+ .close{top:-35px;right:-10px;font-size:28px}
99
+ }
100
+
101
+ /* Safe area untuk iPhone */
102
  body{padding-bottom:env(safe-area-inset-bottom)}
103
+ header{padding-top:env(safe-area-inset-top)}
104
  </style>
105
  </head>
106
  <body>
 
108
  <header>
109
  <div class="nav" role="navigation" aria-label="Menu about">
110
  <div class="logo">PapuaTranslate</div>
111
+
112
+ <button class="menu-toggle" aria-label="Toggle menu" aria-expanded="false">
113
+ <span></span>
114
+ <span></span>
115
+ <span></span>
116
+ </button>
117
+
118
  <nav class="menu">
119
  <a class="pill" href="/">Beranda</a>
120
  <a class="pill" href="/about">About</a>
 
134
  <section class="card">
135
  <h3 class="title">Judul Skripsi</h3>
136
  <p><b>Penerapan Causal Encoder Decoder Natural Language Instructions in Indonesian (CENDOL) untuk Translasi Bahasa Papua ke Bahasa Indonesia Baku</b></p>
137
+ <div class="meta" style="margin-top:16px">
138
+ <div class="label">Dosen Pembimbing</div>
139
+ <div class="value">Viny Christanti Mawardi, S.Kom., M.Kom.</div>
140
+
141
+ <div class="label">Dosen Pendamping</div>
142
+ <div class="value">Manatap Dolok Lauro, S.Kom., M.M.S.I.</div>
143
+
144
+ <div class="label">Program/Kampus</div>
145
+ <div class="value">Teknik Informatika, Universitas Tarumanagara</div>
146
+
147
+ <div class="label">Tahun</div>
148
+ <div class="value">2025</div>
149
  </div>
150
  </section>
151
 
 
154
  <div class="team">
155
  <div class="member">
156
  <div class="avatar" onclick="openModal('m1')">
157
+ <img src="{{ url_for('static', filename='image/pembuat.jpg') }}" alt="Amos Valentino Nababan" onerror="this.style.display='none'; this.parentNode.innerHTML='<div style=&quot;color:#999;padding:40px 0;font-size:14px&quot;>Foto Tidak Ditemukan</div>'"/>
158
  </div>
159
  <div class="m-name">Amos Valentino Nababan</div>
160
  <div class="m-role">Pembuat</div>
161
  </div>
162
+
163
  <div class="member">
164
  <div class="avatar" onclick="openModal('m2')">
165
+ <img src="{{ url_for('static', filename='image/dosen1.jpg') }}" alt="Viny Christanti Mawardi" onerror="this.style.display='none'; this.parentNode.innerHTML='<div style=&quot;color:#999;padding:40px 0;font-size:14px&quot;>Foto Tidak Ditemukan</div>'"/>
166
  </div>
167
  <div class="m-name">Viny Christanti Mawardi, S.Kom., M.Kom.</div>
168
  <div class="m-role">Dosen Pembimbing</div>
169
  </div>
170
+
171
  <div class="member">
172
  <div class="avatar" onclick="openModal('m3')">
173
+ <img src="{{ url_for('static', filename='image/dosen2.jpg') }}" alt="Manatap Dolok Lauro" onerror="this.style.display='none'; this.parentNode.innerHTML='<div style=&quot;color:#999;padding:40px 0;font-size:14px&quot;>Foto Tidak Ditemukan</div>'"/>
174
  </div>
175
  <div class="m-name">Manatap Dolok Lauro, S.Kom., M.M.S.I.</div>
176
  <div class="m-role">Dosen Pendamping</div>
 
180
 
181
  <section class="card">
182
  <h3 class="title">Deskripsi Singkat</h3>
183
+ <p style="margin:0;font-size:clamp(14px,3.5vw,16px);line-height:1.6">Aplikasi ini menerapkan arsitektur <i>Causal Encoder-Decoder</i> (CENDOL) berbasis mT5 dengan LoRA Adapter untuk translasi dialek Papua ke Bahasa Indonesia baku. Sistem ini dirancang untuk memahami dan menerjemahkan berbagai variasi dialek Papua dengan akurasi tinggi.</p>
184
  </section>
185
  </main>
186
 
187
  <footer>
188
+ <div class="wrap" style="text-align:center;padding:20px 0;border-top:1px solid var(--line)">
189
+ <div style="color:var(--sub);font-size:clamp(11px,3vw,13px)">© 2025 PapuaTranslate • Halaman About</div>
190
  </div>
191
  </footer>
192
 
193
  <!-- Modals -->
194
  <div id="m1" class="modal" role="dialog" aria-modal="true" aria-label="Foto Amos">
195
+ <div class="modal-content">
196
+ <span class="close" onclick="closeModal('m1')" aria-label="Tutup">×</span>
197
+ <img src="{{ url_for('static', filename='image/pembuat.jpg') }}" alt="Amos Valentino Nababan">
198
+ <div class="caption">Amos Valentino Nababan — Pembuat</div>
199
+ </div>
200
  </div>
201
+
202
  <div id="m2" class="modal" role="dialog" aria-modal="true" aria-label="Foto Viny">
203
+ <div class="modal-content">
204
+ <span class="close" onclick="closeModal('m2')" aria-label="Tutup">×</span>
205
+ <img src="{{ url_for('static', filename='image/dosen1.jpg') }}" alt="Viny Christanti Mawardi">
206
+ <div class="caption">Viny Christanti Mawardi, S.Kom., M.Kom.</div>
207
+ </div>
208
  </div>
209
+
210
  <div id="m3" class="modal" role="dialog" aria-modal="true" aria-label="Foto Manatap">
211
+ <div class="modal-content">
212
+ <span class="close" onclick="closeModal('m3')" aria-label="Tutup">×</span>
213
+ <img src="{{ url_for('static', filename='image/dosen2.jpg') }}" alt="Manatap Dolok Lauro">
214
+ <div class="caption">Manatap Dolok Lauro, S.Kom., M.M.S.I.</div>
215
+ </div>
216
  </div>
217
 
218
  <script>
219
+ // Mobile menu toggle
220
+ const menuToggle = document.querySelector('.menu-toggle');
221
+ const menu = document.querySelector('.menu');
222
+
223
+ menuToggle.addEventListener('click', () => {
224
+ menu.classList.toggle('active');
225
+ const expanded = menu.classList.contains('active');
226
+ menuToggle.setAttribute('aria-expanded', expanded);
227
+ });
228
+
229
+ function openModal(id){
230
+ document.getElementById(id).style.display = 'flex';
231
+ document.body.style.overflow = 'hidden';
232
+ }
233
+
234
+ function closeModal(id){
235
+ document.getElementById(id).style.display = 'none';
236
+ document.body.style.overflow = 'auto';
237
+ }
238
+
239
+ window.addEventListener('keydown', e => {
240
+ if (e.key === 'Escape') {
241
+ ['m1','m2','m3'].forEach(closeModal);
242
+ }
243
+ });
244
+
245
+ window.addEventListener('click', e => {
246
+ if (e.target.classList.contains('modal')) {
247
+ e.target.style.display = 'none';
248
+ document.body.style.overflow = 'auto';
249
+ }
250
+ });
251
+
252
+ // Close mobile menu when clicking outside
253
+ document.addEventListener('click', (e) => {
254
+ if (!menu.contains(e.target) && !menuToggle.contains(e.target)) {
255
+ menu.classList.remove('active');
256
+ menuToggle.setAttribute('aria-expanded', 'false');
257
+ }
258
+ });
259
  </script>
260
  </body>
261
+ </html>
frontend/index.html CHANGED
@@ -15,58 +15,103 @@
15
  *{box-sizing:border-box}
16
  html,body{margin:0;padding:0;font-family:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Noto Sans,Arial,sans-serif;color:var(--ink);background:var(--bg);line-height:1.6;font-size:var(--fx)}
17
  a{color:inherit;text-decoration:none}
18
- /* Header */
19
- header{position:sticky;top:0;z-index:50;background:var(--card);box-shadow:0 4px 16px rgba(0,0,0,.06)}
 
20
  .nav{max-width:var(--maxw);margin:auto;display:flex;align-items:center;gap:10px;justify-content:space-between;padding:var(--pad)}
21
- .logo{font-weight:800;font-size:clamp(18px,4vw,24px)}
22
- .menu{display:flex;gap:8px;flex-wrap:wrap}
23
- .pill{display:inline-flex;align-items:center;justify-content:center;padding:10px 14px;border:1px solid var(--ink);border-radius:999px;min-height:42px}
24
- .pill:hover{background:var(--ink);color:#fff}
25
- .badge{background:#000;color:#fff;border-radius:999px;padding:6px 12px;font-size:12px}
 
 
26
 
27
- /* Hero */
 
 
 
 
28
  .hero{border-top:1px solid var(--line);border-bottom:1px solid var(--line);background:var(--card)}
29
  .hero-inner{max-width:var(--maxw);margin:auto;text-align:center;padding:clamp(16px,6vw,30px) var(--pad)}
30
- .hero h1{margin:0 0 6px;font-size:clamp(20px,6vw,32px)}
31
- .hero p{margin:0;color:var(--sub);font-size:clamp(13px,3.5vw,15px)}
32
 
33
- /* Main */
34
  .wrap{max-width:var(--maxw);margin:auto;padding:var(--pad)}
35
  .card{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow)}
36
  .card-pad{padding:var(--pad)}
37
  .title{margin:0 0 12px;text-align:center;font-weight:800;font-size:clamp(16px,4.5vw,20px)}
38
 
39
- /* Translator full width */
40
- .input-group{margin-bottom:12px}
41
- .lbl{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px;color:#222;font-weight:700;font-size:clamp(13px,3.6vw,15px)}
42
- .hint{font-size:clamp(11px,3.2vw,13px);color:var(--sub);margin-left:8px}
43
  textarea{
44
- width:100%;min-height:clamp(140px,30vh,220px);resize:vertical;border:1px solid #ddd;border-radius:12px;
45
  padding:14px 12px;font-size:clamp(14px,3.6vw,16px);background:#fafafa;outline:none;transition:border .2s,background .2s;
 
46
  }
47
- textarea:focus{border-color:#555;background:#fff}
48
  .btn{
49
  width:100%;display:inline-flex;align-items:center;justify-content:center;gap:10px;
50
  padding:clamp(12px,3.5vw,14px);border:none;border-radius:12px;background:var(--brand);color:var(--brand-ink);
51
- font-weight:900;cursor:pointer;touch-action:manipulation;min-height:48px;
 
52
  }
53
- .btn[disabled]{opacity:.6}
 
54
  .result{border:1px solid var(--line);background:#fafafa;border-radius:12px;padding:14px;min-height:clamp(84px,16vh,120px)}
55
  .result h4{margin:0 0 6px;font-size:clamp(12px,3.2vw,13px);color:var(--sub)}
56
- .result-text{font-size:clamp(14px,3.6vw,16px);white-space:pre-wrap;color:#000}
57
- .note{font-size:clamp(11px,3vw,12px);color:var(--sub);margin-top:8px}
58
 
59
- /* History under translator */
60
  .history{margin-top:var(--gap)}
61
  .list{margin:0;padding-left:20px}
62
- .list li{margin-bottom:6px}
 
63
 
64
  /* Footer */
65
  footer{margin-top:var(--gap);background:var(--card);border-top:1px solid var(--line)}
66
  .foot{max-width:var(--maxw);margin:auto;padding:var(--pad);text-align:center}
67
  .copy{font-size:clamp(11px,3vw,12px);color:var(--sub)}
68
- /* Safe area iphones */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
  body{padding-bottom:env(safe-area-inset-bottom)}
 
70
  </style>
71
  </head>
72
  <body>
@@ -74,6 +119,13 @@
74
  <header>
75
  <div class="nav" role="navigation" aria-label="Menu utama">
76
  <div class="logo">PapuaTranslate</div>
 
 
 
 
 
 
 
77
  <nav class="menu">
78
  <a class="pill" href="/">Home</a>
79
  <a class="pill" href="/about">About</a>
@@ -114,7 +166,7 @@
114
  <span id="btn-spin" aria-hidden="true" style="display:none">⏳</span>
115
  </button>
116
 
117
- <div class="input-group" style="margin-top:12px">
118
  <div class="lbl">
119
  <label for="indonesia-output">Bahasa Indonesia Baku</label>
120
  <span class="hint">Hasil terjemahan</span>
@@ -148,10 +200,24 @@
148
  </footer>
149
 
150
  <script>
151
- // Auto-resize textarea (nyaman di HP)
 
 
 
 
 
 
 
 
 
 
152
  const ta = document.getElementById('papua-input');
153
- function autoresize(){ ta.style.height='auto'; ta.style.height=(ta.scrollHeight+6)+'px'; }
154
- ta.addEventListener('input', autoresize); window.addEventListener('load', autoresize);
 
 
 
 
155
 
156
  async function translateText() {
157
  const input = document.getElementById('papua-input');
@@ -161,10 +227,16 @@
161
  const btnSpin = document.getElementById('btn-spin');
162
 
163
  const text = input.value.trim();
164
- if (!text) { out.textContent = "Silakan masukkan teks logat Papua."; input.focus(); return; }
 
 
 
 
165
 
166
  out.textContent = "Menerjemahkan…";
167
- btn.disabled = true; btnText.style.display='none'; btnSpin.style.display='inline';
 
 
168
 
169
  try {
170
  const r = await fetch('/translate', {
@@ -177,15 +249,22 @@
177
  if (r.ok && data && (data.mt || data.result)) {
178
  out.textContent = data.mt || data.result;
179
  loadHistory();
180
- // scroll ke hasil di layar kecil
181
- if (window.innerWidth < 700) document.getElementById('indonesia-output-wrap').scrollIntoView({behavior:'smooth', block:'center'});
 
 
 
 
 
182
  } else {
183
  out.textContent = 'Error: ' + ((data && (data.error || data.detail)) || 'Terjadi kesalahan.');
184
  }
185
  } catch (e) {
186
  out.textContent = 'Gagal terhubung ke server.';
187
  } finally {
188
- btn.disabled = false; btnText.style.display='inline'; btnSpin.style.display='none';
 
 
189
  }
190
  }
191
 
@@ -212,6 +291,14 @@
212
  ta.addEventListener('keydown', (e)=>{
213
  if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) translateText();
214
  });
 
 
 
 
 
 
 
 
215
  </script>
216
  </body>
217
- </html>
 
15
  *{box-sizing:border-box}
16
  html,body{margin:0;padding:0;font-family:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Noto Sans,Arial,sans-serif;color:var(--ink);background:var(--bg);line-height:1.6;font-size:var(--fx)}
17
  a{color:inherit;text-decoration:none}
18
+
19
+ /* Header Responsif */
20
+ header{position:sticky;top:0;z-index:50;background:var(--card);box-shadow:0 4px 16px rgba(0,0,0,.06);backdrop-filter:blur(8px)}
21
  .nav{max-width:var(--maxw);margin:auto;display:flex;align-items:center;gap:10px;justify-content:space-between;padding:var(--pad)}
22
+ .logo{font-weight:800;font-size:clamp(18px,4vw,24px);white-space:nowrap}
23
+
24
+ /* Menu Responsif */
25
+ .menu{display:flex;gap:6px;flex-wrap:wrap;justify-content:flex-end}
26
+ .pill{display:inline-flex;align-items:center;justify-content:center;padding:8px 12px;border:1px solid var(--ink);border-radius:999px;min-height:38px;font-size:clamp(12px,3vw,14px);white-space:nowrap;transition:all 0.2s ease}
27
+ .pill:hover{background:var(--ink);color:#fff;transform:translateY(-1px)}
28
+ .badge{background:#000;color:#fff;border-radius:999px;padding:4px 8px;font-size:11px;min-width:20px;text-align:center}
29
 
30
+ /* Mobile Menu */
31
+ .menu-toggle{display:none;flex-direction:column;gap:3px;background:none;border:none;cursor:pointer;padding:8px}
32
+ .menu-toggle span{width:20px;height:2px;background:var(--ink);transition:0.3s}
33
+
34
+ /* Hero Responsif */
35
  .hero{border-top:1px solid var(--line);border-bottom:1px solid var(--line);background:var(--card)}
36
  .hero-inner{max-width:var(--maxw);margin:auto;text-align:center;padding:clamp(16px,6vw,30px) var(--pad)}
37
+ .hero h1{margin:0 0 6px;font-size:clamp(20px,6vw,32px);line-height:1.3}
38
+ .hero p{margin:0;color:var(--sub);font-size:clamp(13px,3.5vw,15px);max-width:600px;margin-inline:auto}
39
 
40
+ /* Main Content */
41
  .wrap{max-width:var(--maxw);margin:auto;padding:var(--pad)}
42
  .card{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow)}
43
  .card-pad{padding:var(--pad)}
44
  .title{margin:0 0 12px;text-align:center;font-weight:800;font-size:clamp(16px,4.5vw,20px)}
45
 
46
+ /* Translator Responsif */
47
+ .input-group{margin-bottom:16px}
48
+ .lbl{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;margin-bottom:8px;color:#222;font-weight:700;font-size:clamp(13px,3.6vw,15px);gap:8px}
49
+ .hint{font-size:clamp(11px,3.2vw,13px);color:var(--sub)}
50
  textarea{
51
+ width:100%;min-height:clamp(120px,25vh,200px);resize:vertical;border:1px solid #ddd;border-radius:12px;
52
  padding:14px 12px;font-size:clamp(14px,3.6vw,16px);background:#fafafa;outline:none;transition:border .2s,background .2s;
53
+ font-family:inherit;
54
  }
55
+ textarea:focus{border-color:#555;background:#fff;box-shadow:0 0 0 2px rgba(0,0,0,0.1)}
56
  .btn{
57
  width:100%;display:inline-flex;align-items:center;justify-content:center;gap:10px;
58
  padding:clamp(12px,3.5vw,14px);border:none;border-radius:12px;background:var(--brand);color:var(--brand-ink);
59
+ font-weight:700;cursor:pointer;touch-action:manipulation;min-height:48px;font-size:clamp(14px,3.6vw,16px);
60
+ transition:all 0.2s ease;
61
  }
62
+ .btn:hover:not([disabled]){transform:translateY(-1px);box-shadow:0 4px 12px rgba(0,0,0,0.15)}
63
+ .btn[disabled]{opacity:.6;cursor:not-allowed;transform:none}
64
  .result{border:1px solid var(--line);background:#fafafa;border-radius:12px;padding:14px;min-height:clamp(84px,16vh,120px)}
65
  .result h4{margin:0 0 6px;font-size:clamp(12px,3.2vw,13px);color:var(--sub)}
66
+ .result-text{font-size:clamp(14px,3.6vw,16px);white-space:pre-wrap;color:#000;line-height:1.5}
67
+ .note{font-size:clamp(11px,3vw,12px);color:var(--sub);margin-top:8px;text-align:center}
68
 
69
+ /* History Responsif */
70
  .history{margin-top:var(--gap)}
71
  .list{margin:0;padding-left:20px}
72
+ .list li{margin-bottom:8px;font-size:clamp(13px,3.5vw,15px);line-height:1.4}
73
+ .list li small{font-size:clamp(11px,3vw,12px);color:var(--sub)}
74
 
75
  /* Footer */
76
  footer{margin-top:var(--gap);background:var(--card);border-top:1px solid var(--line)}
77
  .foot{max-width:var(--maxw);margin:auto;padding:var(--pad);text-align:center}
78
  .copy{font-size:clamp(11px,3vw,12px);color:var(--sub)}
79
+
80
+ /* Mobile Styles */
81
+ @media (max-width: 768px) {
82
+ .nav{flex-wrap:wrap;gap:12px}
83
+ .menu-toggle{display:flex}
84
+ .menu{display:none;width:100%;flex-direction:column;gap:8px}
85
+ .menu.active{display:flex}
86
+ .pill{justify-content:center;min-height:44px;font-size:14px}
87
+
88
+ .hero-inner{padding:clamp(14px,5vw,24px) var(--pad)}
89
+ .hero h1{font-size:clamp(18px,5vw,24px)}
90
+
91
+ .lbl{flex-direction:column;align-items:flex-start;gap:4px}
92
+ .hint{margin-left:0}
93
+
94
+ textarea{min-height:100px;font-size:16px} /* Prevent zoom on iOS */
95
+ }
96
+
97
+ @media (max-width: 480px) {
98
+ .nav{padding:12px}
99
+ .logo{font-size:16px}
100
+ .pill{padding:10px 16px;font-size:13px}
101
+
102
+ .hero h1{font-size:20px}
103
+ .hero p{font-size:13px}
104
+
105
+ .card-pad{padding:16px}
106
+ .title{font-size:18px}
107
+
108
+ .list{padding-left:16px}
109
+ .list li{font-size:14px}
110
+ }
111
+
112
+ /* Safe area untuk iPhone */
113
  body{padding-bottom:env(safe-area-inset-bottom)}
114
+ header{padding-top:env(safe-area-inset-top)}
115
  </style>
116
  </head>
117
  <body>
 
119
  <header>
120
  <div class="nav" role="navigation" aria-label="Menu utama">
121
  <div class="logo">PapuaTranslate</div>
122
+
123
+ <button class="menu-toggle" aria-label="Toggle menu" aria-expanded="false">
124
+ <span></span>
125
+ <span></span>
126
+ <span></span>
127
+ </button>
128
+
129
  <nav class="menu">
130
  <a class="pill" href="/">Home</a>
131
  <a class="pill" href="/about">About</a>
 
166
  <span id="btn-spin" aria-hidden="true" style="display:none">⏳</span>
167
  </button>
168
 
169
+ <div class="input-group" style="margin-top:16px">
170
  <div class="lbl">
171
  <label for="indonesia-output">Bahasa Indonesia Baku</label>
172
  <span class="hint">Hasil terjemahan</span>
 
200
  </footer>
201
 
202
  <script>
203
+ // Mobile menu toggle
204
+ const menuToggle = document.querySelector('.menu-toggle');
205
+ const menu = document.querySelector('.menu');
206
+
207
+ menuToggle.addEventListener('click', () => {
208
+ menu.classList.toggle('active');
209
+ const expanded = menu.classList.contains('active');
210
+ menuToggle.setAttribute('aria-expanded', expanded);
211
+ });
212
+
213
+ // Auto-resize textarea
214
  const ta = document.getElementById('papua-input');
215
+ function autoresize(){
216
+ ta.style.height='auto';
217
+ ta.style.height=(ta.scrollHeight + 6)+'px';
218
+ }
219
+ ta.addEventListener('input', autoresize);
220
+ window.addEventListener('load', autoresize);
221
 
222
  async function translateText() {
223
  const input = document.getElementById('papua-input');
 
227
  const btnSpin = document.getElementById('btn-spin');
228
 
229
  const text = input.value.trim();
230
+ if (!text) {
231
+ out.textContent = "Silakan masukkan teks logat Papua.";
232
+ input.focus();
233
+ return;
234
+ }
235
 
236
  out.textContent = "Menerjemahkan…";
237
+ btn.disabled = true;
238
+ btnText.style.display='none';
239
+ btnSpin.style.display='inline';
240
 
241
  try {
242
  const r = await fetch('/translate', {
 
249
  if (r.ok && data && (data.mt || data.result)) {
250
  out.textContent = data.mt || data.result;
251
  loadHistory();
252
+ // Scroll ke hasil di layar kecil
253
+ if (window.innerWidth < 768) {
254
+ document.getElementById('indonesia-output-wrap').scrollIntoView({
255
+ behavior: 'smooth',
256
+ block: 'center'
257
+ });
258
+ }
259
  } else {
260
  out.textContent = 'Error: ' + ((data && (data.error || data.detail)) || 'Terjadi kesalahan.');
261
  }
262
  } catch (e) {
263
  out.textContent = 'Gagal terhubung ke server.';
264
  } finally {
265
+ btn.disabled = false;
266
+ btnText.style.display='inline';
267
+ btnSpin.style.display='none';
268
  }
269
  }
270
 
 
291
  ta.addEventListener('keydown', (e)=>{
292
  if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) translateText();
293
  });
294
+
295
+ // Close mobile menu when clicking outside
296
+ document.addEventListener('click', (e) => {
297
+ if (!menu.contains(e.target) && !menuToggle.contains(e.target)) {
298
+ menu.classList.remove('active');
299
+ menuToggle.setAttribute('aria-expanded', 'false');
300
+ }
301
+ });
302
  </script>
303
  </body>
304
+ </html>