Anupriya commited on
Commit
f8794d4
·
1 Parent(s): c3fcb70

Frontend update and image

Browse files
src/app/app-routing.module.ts CHANGED
@@ -17,14 +17,18 @@ import { AuthComponent } from './auth/auth.component';
17
  import { authGuard } from './auth/auth.guard';
18
  import { rootRedirectGuard } from './auth/root-redirect.guard';
19
  import { RootRedirectComponent } from './root-redirect/root-redirect.component';
 
20
 
21
 
22
  // Define and export routes
23
  export const routes: Routes = [
24
- //{ path: '', redirectTo: 'authentication', pathMatch: 'full' },
25
- { path: '', component: RootRedirectComponent, canActivate: [rootRedirectGuard], pathMatch: 'full' },
26
 
27
- { path: 'home', component: HomeComponent, canActivate: [authGuard] },
 
 
 
28
  { path: 'chat', component: ChatComponent, canActivate: [authGuard] },
29
  { path: 'generate-questions', component: GenerateQuestionsComponent, canActivate: [authGuard] },
30
  { path: 'voice', component: VoiceComponent, canActivate: [authGuard] },
@@ -33,14 +37,15 @@ export const routes: Routes = [
33
  { path: 'vocabulary-builder', component: VocabularyBuilderComponent, canActivate: [authGuard] },
34
  { path: 'findword', component: FindwordComponent, canActivate: [authGuard] },
35
  { path: 'reading', component: ReadingComponent, canActivate: [authGuard] },
 
 
36
  { path: 'authentication', component: AuthenticationComponent },
37
- { path: 'auth', component: AuthComponent },
38
- { path: '**', redirectTo: 'auth' },
 
39
 
40
- //{ path: 'generate-questions', component: GenerateQuestionsComponent },
41
- //{ path: 'chat', component: ChatComponent},
42
- //{ path: 'findword', component: FindwordComponent },
43
- //{ path: 'reading', component: ReadingComponent},
44
 
45
 
46
  ];
 
17
  import { authGuard } from './auth/auth.guard';
18
  import { rootRedirectGuard } from './auth/root-redirect.guard';
19
  import { RootRedirectComponent } from './root-redirect/root-redirect.component';
20
+ import { SignInComponent } from './sign-in/sign-in.component';
21
 
22
 
23
  // Define and export routes
24
  export const routes: Routes = [
25
+ // Default public home page
26
+ { path: '', component: HomeComponent, pathMatch: 'full' },
27
 
28
+ // Public home route (no guard)
29
+ { path: 'home', component: HomeComponent },
30
+
31
+ // Protected module routes
32
  { path: 'chat', component: ChatComponent, canActivate: [authGuard] },
33
  { path: 'generate-questions', component: GenerateQuestionsComponent, canActivate: [authGuard] },
34
  { path: 'voice', component: VoiceComponent, canActivate: [authGuard] },
 
37
  { path: 'vocabulary-builder', component: VocabularyBuilderComponent, canActivate: [authGuard] },
38
  { path: 'findword', component: FindwordComponent, canActivate: [authGuard] },
39
  { path: 'reading', component: ReadingComponent, canActivate: [authGuard] },
40
+
41
+ // Auth routes
42
  { path: 'authentication', component: AuthenticationComponent },
43
+ // Use integrated SignIn (with embedded SignUp) for auth and login aliases
44
+ { path: 'auth', component: SignInComponent },
45
+ { path: 'login', component: SignInComponent },
46
 
47
+ // Fallback
48
+ { path: '**', redirectTo: '' },
 
 
49
 
50
 
51
  ];
src/app/app.module.ts CHANGED
@@ -16,9 +16,9 @@ import { FindwordComponent } from './findword/findword.component';
16
  import { ReadingComponent } from './reading/reading.component';
17
  import { AuthenticationComponent } from './authentication/authentication.component';
18
  import { AuthComponent } from './auth/auth.component';
19
-
20
  import { RootRedirectComponent } from './root-redirect/root-redirect.component';
21
-
 
22
 
23
 
24
 
@@ -48,7 +48,8 @@ import { RootRedirectComponent } from './root-redirect/root-redirect.component';
48
  AppRoutingModule,
49
  FormsModule,
50
  HttpClientModule,
51
-
 
52
  ],
53
  providers: [],
54
  bootstrap: [AppComponent] // Bootstrap AppComponent
 
16
  import { ReadingComponent } from './reading/reading.component';
17
  import { AuthenticationComponent } from './authentication/authentication.component';
18
  import { AuthComponent } from './auth/auth.component';
 
19
  import { RootRedirectComponent } from './root-redirect/root-redirect.component';
20
+ import { HeaderComponent } from './shared/header/header.component';
21
+ import { SignInComponent } from './sign-in/sign-in.component';
22
 
23
 
24
 
 
48
  AppRoutingModule,
49
  FormsModule,
50
  HttpClientModule,
51
+ HeaderComponent, // standalone component imported for use in declared components
52
+ SignInComponent // import the standalone sign-in (which embeds sign-up)
53
  ],
54
  providers: [],
55
  bootstrap: [AppComponent] // Bootstrap AppComponent
src/app/auth/auth.service.ts CHANGED
@@ -85,9 +85,14 @@ export class AuthService {
85
  console.log('🔙 Response from backend:', response);
86
  this.clearTokens();
87
  this.clearAutoRefresh(); // ✅ Stop auto-refresh
 
88
  }),
89
  catchError(error => {
90
  console.error('❌ Error from backend:', error);
 
 
 
 
91
  return throwError(() => error);
92
  })
93
  );
 
85
  console.log('🔙 Response from backend:', response);
86
  this.clearTokens();
87
  this.clearAutoRefresh(); // ✅ Stop auto-refresh
88
+ this.setLoggedIn(false); // ✅ Reflect logout in UI
89
  }),
90
  catchError(error => {
91
  console.error('❌ Error from backend:', error);
92
+ // Still ensure local state reflects logout
93
+ this.clearTokens();
94
+ this.clearAutoRefresh();
95
+ this.setLoggedIn(false);
96
  return throwError(() => error);
97
  })
98
  );
src/app/home/home.component.css CHANGED
@@ -1,13 +1,8 @@
1
-
2
-
3
- /* Hero Section with Background Image */
4
  .hero {
5
  background: url('/assets/images/home/background.png') no-repeat;
6
  flex: 1;
7
  }
8
 
9
-
10
-
11
  .hero-text {
12
  color: white;
13
  text-align: left;
@@ -16,16 +11,16 @@
16
  transform: translate(1vw, 17vw);
17
  }
18
 
19
-
20
  h1 {
21
  font-size: 7vw;
22
  font-family: Amonk_Outline;
 
23
  }
24
 
25
  .hero-text h2 {
26
  font-size: 1.5vw;
27
  font-weight: 600;
28
- color: #00ff00; /* Green text */
29
  }
30
 
31
  .hero-text p {
@@ -33,17 +28,13 @@ h1 {
33
  line-height: 1.5vw;
34
  }
35
 
36
-
37
-
38
-
39
- /* General Navbar Styles */
40
  .navbar {
41
  background-color: #fff;
42
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
43
  position: fixed;
44
- font-size: 1vw; /* Customize font size for navbar */
45
  width: 100%;
46
- z-index: 1000; /* Ensures it stays above other content */
47
  }
48
 
49
  .navbar-container {
@@ -53,29 +44,27 @@ h1 {
53
  padding: 0vw 1vw;
54
  }
55
 
56
- /* Left: Logo */
57
  .logo {
58
  display: flex;
59
  align-items: center;
60
- gap: 1vw; /* Space between logo image and text */
61
  }
62
 
63
  .logo img {
64
- height: 5vw; /* Adjust image size using vw */
65
  width: auto;
66
  cursor: pointer;
67
  }
68
 
69
  .logo span {
70
- font-size: 1.5vw; /* Customize font size for logo text */
71
  font-weight: bold;
72
  color: #007bff;
73
  }
74
 
75
- /* Center: Navigation Links */
76
  .nav-links {
77
  display: flex;
78
- gap: 2vw; /* Space between nav links */
79
  list-style: none;
80
  margin: 0;
81
  padding: 0;
@@ -86,71 +75,140 @@ h1 {
86
  .nav-links li a {
87
  text-decoration: none;
88
  color: #333;
89
- font-size: 1.5vw; /* Customize font size for nav links */
90
  font-weight: 700;
91
  }
92
 
93
- .nav-links li a:hover {
 
 
 
 
 
 
 
94
  color: #007bff;
95
  }
96
 
 
 
 
97
 
 
 
 
 
 
98
 
99
- /* Footer Section */
100
- .footer {
101
- background-color: #2c2c2c;
102
- color: #fff;
103
- padding: 20px 10px;
104
- text-align: center;
105
- width: 100%;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
106
  position: relative;
107
- bottom: -37vw;
108
  }
109
 
110
- /* Social Media Icons Styling */
111
- .social-media {
112
- margin-top: 10px;
113
- display: flex;
 
 
 
 
 
 
 
114
  justify-content: center;
115
- gap: 15px;
 
 
116
  }
117
 
118
- .social-media a {
119
- color: #fff;
120
- font-size: 20px;
121
- text-decoration: none;
122
- transition: color 0.3s ease;
123
- }
 
 
 
 
 
 
124
 
125
- .social-media a:hover {
126
- color: #1e90ff; /* Change hover color as desired */
127
- }
 
 
 
 
 
 
 
 
128
 
129
- .social-media img {
130
- width: 3vw; /* Adjust the size of the icons */
131
- height: 3vw;
132
- transition: transform 0.3s ease;
133
- }
 
134
 
135
- .social-media img:hover {
136
- transform: scale(1.1); /* Slightly enlarge on hover */
137
- }
 
138
 
139
- /* Responsive Design */
140
- @media (max-width: 768px) {
141
- .social-media {
142
- gap: 10px;
143
- }
144
 
145
- .social-media a {
146
- font-size: 18px;
147
- }
 
148
 
149
- .social-media img {
150
- width: 5vw;
151
- height: 5vw;
152
- }
 
 
 
 
 
 
 
 
 
 
 
153
  }
 
154
  .card {
155
  background-color: white;
156
  border-radius: 10px;
@@ -160,7 +218,7 @@ h1 {
160
  width: 100%;
161
  max-width: 460px;
162
  margin: auto;
163
- height:31vw;
164
  }
165
 
166
  .card:hover {
@@ -178,46 +236,6 @@ h1 {
178
  text-align: left;
179
  }
180
 
181
- .card-title {
182
- font-size: 1.5vw;
183
- font-weight: bold;
184
- color: #1a8ec1;
185
- }
186
-
187
- .content-wrapper {
188
- max-height: 9vw;
189
- overflow-y: hidden;
190
- transition: max-height 0.3s ease;
191
- }
192
-
193
- .content-wrapper.expanded {
194
- max-height: 9vw;
195
- overflow-y: auto;
196
- padding-right: 0.5vw;
197
- }
198
-
199
- .content-wrapper p {
200
- font-size: 1vw;
201
- text-align:justify;
202
- color: black;
203
- /*font-weight: 700;*/
204
- }
205
-
206
-
207
- .view-more-link {
208
- display: inline-block;
209
- margin-top: 0.5vw;
210
- font-size: 1vw;
211
- color: #007bff;
212
- cursor: pointer;
213
- text-decoration: underline;
214
- }
215
-
216
- .view-more-link:hover {
217
- color: #0056b3;
218
- }
219
-
220
-
221
  .cards-section {
222
  text-align: center;
223
  padding: 3vw 2vw;
@@ -232,7 +250,6 @@ h1 {
232
  margin-bottom: 2vw;
233
  }
234
 
235
-
236
  .cards-container {
237
  gap: 3vw;
238
  justify-content: center;
@@ -242,17 +259,16 @@ h1 {
242
  width: 80%;
243
  }
244
 
245
-
246
  .card-buttons {
247
  display: flex;
248
  gap: 1vw;
249
  margin-top: 1vw;
250
  flex-wrap: nowrap;
251
- justify-content: space-between;
252
  }
253
 
254
  .card-button {
255
- white-space: nowrap; /* Prevent text wrapping */
256
  padding: 0.5vw 0.5vw;
257
  font-size: 1vw;
258
  font-weight: bold;
@@ -269,24 +285,287 @@ h1 {
269
  color: white;
270
  }
271
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
272
 
273
- .nav-link--disabled {
274
- opacity: 0.6;
275
- cursor: not-allowed;
276
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
277
  }
278
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
279
 
280
- /* Apply the same look to <a> and the disabled <span class="nav-link"> */
281
- .nav-links li a,
282
- .nav-links li .nav-link {
283
  text-decoration: none;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
284
  color: #333;
285
- font-size: 1.5vw;
286
- font-weight: 700;
 
 
 
287
  }
288
 
289
- .nav-links li a:hover,
290
- .nav-links li .nav-link:hover {
291
- color: #007bff;
 
292
  }
 
 
 
 
1
  .hero {
2
  background: url('/assets/images/home/background.png') no-repeat;
3
  flex: 1;
4
  }
5
 
 
 
6
  .hero-text {
7
  color: white;
8
  text-align: left;
 
11
  transform: translate(1vw, 17vw);
12
  }
13
 
 
14
  h1 {
15
  font-size: 7vw;
16
  font-family: Amonk_Outline;
17
+ margin-bottom: 3vw;
18
  }
19
 
20
  .hero-text h2 {
21
  font-size: 1.5vw;
22
  font-weight: 600;
23
+ color: #00ff00;
24
  }
25
 
26
  .hero-text p {
 
28
  line-height: 1.5vw;
29
  }
30
 
 
 
 
 
31
  .navbar {
32
  background-color: #fff;
33
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
34
  position: fixed;
35
+ font-size: 1vw;
36
  width: 100%;
37
+ z-index: 1000;
38
  }
39
 
40
  .navbar-container {
 
44
  padding: 0vw 1vw;
45
  }
46
 
 
47
  .logo {
48
  display: flex;
49
  align-items: center;
50
+ gap: 1vw;
51
  }
52
 
53
  .logo img {
54
+ height: 5vw;
55
  width: auto;
56
  cursor: pointer;
57
  }
58
 
59
  .logo span {
60
+ font-size: 1.5vw;
61
  font-weight: bold;
62
  color: #007bff;
63
  }
64
 
 
65
  .nav-links {
66
  display: flex;
67
+ gap: 2vw;
68
  list-style: none;
69
  margin: 0;
70
  padding: 0;
 
75
  .nav-links li a {
76
  text-decoration: none;
77
  color: #333;
78
+ font-size: 1.5vw;
79
  font-weight: 700;
80
  }
81
 
82
+ .nav-links li .nav-link {
83
+ text-decoration: none;
84
+ color: #333;
85
+ font-size: 1.5vw;
86
+ font-weight: 700;
87
+ }
88
+
89
+ .nav-links li .nav-link:hover {
90
  color: #007bff;
91
  }
92
 
93
+ .nav-links li a:hover {
94
+ color: #007bff;
95
+ }
96
 
97
+ .nav-link--disabled {
98
+ opacity: 0.6;
99
+ cursor: not-allowed;
100
+ pointer-events: none;
101
+ }
102
 
103
+ .login-button {
104
+ display: inline-flex;
105
+ align-items: center;
106
+ justify-content: center;
107
+ width: 2.4vw;
108
+ height: 2.4vw;
109
+ border-radius: 50%;
110
+ border: 2px solid #006780;
111
+ color: #006780;
112
+ background: #ffffff;
113
+ font-size: 1.2vw;
114
+ text-decoration: none;
115
+ transition: background 0.2s, color 0.2s, transform 0.2s, box-shadow 0.2s;
116
+ box-shadow: 0 2px 8px rgba(93,145,195,0.12);
117
+ }
118
+
119
+ .login-button:hover {
120
+ background: #006780;
121
+ color: #ffffff;
122
+ transform: translateY(-1px) scale(1.05);
123
+ box-shadow: 0 4px 16px rgba(93,145,195,0.18);
124
+ }
125
+
126
+ .nav-actions {
127
  position: relative;
128
+ padding-right: 1.5vw;
129
  }
130
 
131
+ .avatar {
132
+ width: 2.4vw;
133
+ height: 2.4vw;
134
+ min-width: 32px;
135
+ min-height: 32px;
136
+ border-radius: 50%;
137
+ background: #e6f2ff;
138
+ border: 2px solid #137ec4;
139
+ color: #137ec4;
140
+ display: inline-flex;
141
+ align-items: center;
142
  justify-content: center;
143
+ font-weight: 800;
144
+ cursor: pointer;
145
+ box-shadow: 0 2px 8px rgba(93,145,195,0.12);
146
  }
147
 
148
+ .avatar-lg {
149
+ width: 44px;
150
+ height: 44px;
151
+ border-radius: 50%;
152
+ background: #e6f2ff;
153
+ border: 2px solid #137ec4;
154
+ color: #137ec4;
155
+ display: inline-flex;
156
+ align-items: center;
157
+ justify-content: center;
158
+ font-weight: 800;
159
+ }
160
 
161
+ .account-menu {
162
+ position: absolute;
163
+ top: 3.2vw;
164
+ right: 0;
165
+ min-width: 260px;
166
+ background: #fff;
167
+ border-radius: 12px;
168
+ box-shadow: 0 12px 24px rgba(0,0,0,0.12);
169
+ padding: 12px;
170
+ z-index: 1200;
171
+ }
172
 
173
+ .account-header {
174
+ display: flex;
175
+ gap: 12px;
176
+ align-items: center;
177
+ padding: 6px 6px 10px 6px;
178
+ }
179
 
180
+ .account-meta {
181
+ display: flex;
182
+ flex-direction: column;
183
+ }
184
 
185
+ .account-name {
186
+ font-weight: 800;
187
+ color: #111827;
188
+ }
 
189
 
190
+ .account-email {
191
+ color: #6b7280;
192
+ font-size: 0.9rem;
193
+ }
194
 
195
+ .account-item {
196
+ padding: 10px;
197
+ border-radius: 8px;
198
+ cursor: pointer;
199
+ color: #111827;
200
+ }
201
+
202
+ .account-item:hover {
203
+ background: #f3f4f6;
204
+ }
205
+
206
+ .account-menu hr {
207
+ margin: 8px 0;
208
+ border: none;
209
+ border-top: 1px solid #e5e7eb;
210
  }
211
+
212
  .card {
213
  background-color: white;
214
  border-radius: 10px;
 
218
  width: 100%;
219
  max-width: 460px;
220
  margin: auto;
221
+ cursor: pointer;
222
  }
223
 
224
  .card:hover {
 
236
  text-align: left;
237
  }
238
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
239
  .cards-section {
240
  text-align: center;
241
  padding: 3vw 2vw;
 
250
  margin-bottom: 2vw;
251
  }
252
 
 
253
  .cards-container {
254
  gap: 3vw;
255
  justify-content: center;
 
259
  width: 80%;
260
  }
261
 
 
262
  .card-buttons {
263
  display: flex;
264
  gap: 1vw;
265
  margin-top: 1vw;
266
  flex-wrap: nowrap;
267
+ justify-content: center;
268
  }
269
 
270
  .card-button {
271
+ white-space: nowrap;
272
  padding: 0.5vw 0.5vw;
273
  font-size: 1vw;
274
  font-weight: bold;
 
285
  color: white;
286
  }
287
 
288
+ .user-guide-overlay {
289
+ position: fixed;
290
+ inset: 0;
291
+ background: rgba(0, 0, 0, 0.35);
292
+ z-index: 2000;
293
+ }
294
+
295
+ .user-guide-modal {
296
+ position: fixed;
297
+ top: 52%;
298
+ left: 50%;
299
+ transform: translate(-50%, -50%);
300
+ height: 38vw;
301
+ width: 50vw;
302
+ background: linear-gradient(135deg, #fff 80%, #e3fcec 100%);
303
+ color: #222;
304
+ box-shadow: 0 12px 40px rgba(93, 145, 195, .22);
305
+ border-radius: 18px;
306
+ padding: 1vw;
307
+ z-index: 2001;
308
+ overflow: visible;
309
+ border: 10px solid #009688;
310
+ box-sizing: border-box;
311
+ }
312
 
313
+ .user-guide-modal > ul,
314
+ .user-guide-modal > ol {
315
+ max-height: 34.6vw;
316
+ overflow-y: auto;
317
+ padding-right: 0.5vw;
318
+ margin: 0;
319
+ }
320
+
321
+ .user-guide-close-icon {
322
+ position: absolute;
323
+ top: -22px;
324
+ right: -22px;
325
+ background: #009688;
326
+ border: none;
327
+ width: 44px;
328
+ height: 44px;
329
+ border-radius: 50%;
330
+ display: flex;
331
+ align-items: center;
332
+ justify-content: center;
333
+ font-size: 2vw;
334
+ color: black;
335
+ cursor: pointer;
336
+ z-index: 2010;
337
+ box-shadow: 0 2px 8px rgba(93,145,195,0.18);
338
+ transition: background 0.2s, color 0.2s;
339
+ }
340
+
341
+ .user-guide-close-icon:hover {
342
+ background: white;
343
+ color: black;
344
+ }
345
+
346
+ .user-guide-modal li {
347
+ line-height: 1.7;
348
+ font-size: 1.1vw;
349
+ background: rgba(93, 145, 195, .07);
350
+ padding: .5vw .5vw;
351
+ box-shadow: 0 2px 8px rgba(93, 145, 195, .06);
352
  }
353
 
354
+ .user-guide-modal li b {
355
+ color: #2b6296;
356
+ font-size: 1.15vw;
357
+ }
358
+
359
+ footer {
360
+ background: linear-gradient(to right, #011022, #01030a);
361
+ color: #fff;
362
+ text-align: center;
363
+ padding: 15px 10px;
364
+ text-align: center;
365
+ width: 100%;
366
+ position: relative;
367
+ margin-top: 28vw;
368
+ font-size: 18px;
369
+ font-weight: 700;
370
+ font-family: Inter, Segoe UI, -apple-system, BlinkMacSystemFont, sans-serif;
371
+ }
372
+
373
+ p.footer {
374
+ line-height: 1;
375
+ margin-top: 1vw;
376
+ }
377
 
378
+ footer a {
379
+ color: #fff;
 
380
  text-decoration: none;
381
+ margin: 0 5px;
382
+ margin-bottom: 0.5vw;
383
+ display: inline-block;
384
+ }
385
+
386
+ footer .social-icons {
387
+ margin-top: 0.5vw;
388
+ display: flex;
389
+ justify-content: center;
390
+ gap: 28px;
391
+ flex-wrap: wrap;
392
+ }
393
+
394
+ footer .social-icons .social-icon {
395
+ width: 42px;
396
+ height: 42px;
397
+ display: inline-flex;
398
+ align-items: center;
399
+ justify-content: center;
400
+ border-radius: 50%;
401
+ background-color: #fff;
402
+ color: #38bdf8;
403
+ font-size: 18px;
404
+ box-shadow: 0 0 0 1px #214055, 0 4px 14px #0006;
405
+ transition: background-color 0.25s, color 0.25s, transform 0.25s, box-shadow 0.25s;
406
+ text-decoration: none;
407
+ }
408
+
409
+ footer .social-icons .social-icon:hover {
410
+ background-color: #38bdf8;
411
+ color: #fff;
412
+ transform: translateY(-4px);
413
+ box-shadow: 0 6px 20px #38bdf8aa, 0 0 0 2px #38bdf8 inset;
414
+ }
415
+
416
+ footer .social-icons .social-icon.facebook {
417
+ color: #1877f2;
418
+ }
419
+
420
+ footer .social-icons .social-icon.youtube {
421
+ color: #ff0000;
422
+ }
423
+
424
+ footer .social-icons .social-icon.linkedin {
425
+ color: #0a66c2;
426
+ }
427
+
428
+ footer .social-icons .social-icon.instagram {
429
+ color: #fd5949;
430
+ }
431
+
432
+ footer .social-icons .social-icon.facebook:hover {
433
+ background-color: #1877f2;
434
+ color: #fff;
435
+ }
436
+
437
+ footer .social-icons .social-icon.youtube:hover {
438
+ background-color: #ff0000;
439
+ color: #fff;
440
+ }
441
+
442
+ footer .social-icons .social-icon.linkedin:hover {
443
+ background-color: #0a66c2;
444
+ color: #fff;
445
+ }
446
+
447
+ footer .social-icons .social-icon.instagram:hover {
448
+ background: radial-gradient(circle at 30% 110%, #fdf497 0%, #fd5949 45%, #d6249f 60%, #285aeb 90%);
449
+ color: #fff;
450
+ filter: brightness(1.15);
451
+ box-shadow: 0 6px 22px rgba(253, 89, 73, .6);
452
+ }
453
+
454
+ @media (max-width: 600px) {
455
+ .user-guide-modal {
456
+ width: 90vw;
457
+ height: 60vw;
458
+ border-radius: 12px;
459
+ padding: 4vw 2vw 2vw 2vw;
460
+ }
461
+
462
+ .user-guide-modal > ul,
463
+ .user-guide-modal > ol {
464
+ max-height: 52vw;
465
+ overflow-y: auto;
466
+ padding-right: 1.5vw;
467
+ }
468
+
469
+ .user-guide-close-icon {
470
+ font-size: 6vw;
471
+ top: 2vw;
472
+ right: 2vw;
473
+ width: 6vw;
474
+ height: 6vw;
475
+ display: flex;
476
+ align-items: center;
477
+ justify-content: center;
478
+ line-height: 1;
479
+ }
480
+
481
+ .card-button, .know-more-btn {
482
+ font-size: 2.8vw;
483
+ padding: 8px 12px;
484
+ }
485
+ }
486
+
487
+ .card-button, .know-more-btn {
488
+ padding: 10px 18px;
489
+ background: #006780;
490
+ color: #fff;
491
+ border: 2px solid #006780;
492
+ border-radius: 8px;
493
+ font-size: 1vw;
494
+ font-weight: bold;
495
+ cursor: pointer;
496
+ margin: 0 6px 6px 0;
497
+ box-shadow: 0 2px 8px rgba(93,145,195,0.12);
498
+ transition: background 0.2s, box-shadow 0.2s, transform 0.2s, color 0.2s;
499
+ }
500
+
501
+ .card-button:hover, .know-more-btn:hover {
502
+ background: #ffffff;
503
+ color: #006780;
504
+ box-shadow: 0 4px 16px rgba(93,145,195,0.18);
505
+ transform: scale(1.05);
506
+ }
507
+
508
+ .privacy-popup-content {
509
+ max-height: 60vh;
510
+ overflow-y: auto;
511
+ padding-right: 1vw;
512
+ }
513
+
514
+ .main-menu-dropdown {
515
+ position: relative;
516
+ }
517
+
518
+ .main-menu-dropdown > .nav-link {
519
+ display: flex;
520
+ align-items: center;
521
+ cursor: pointer;
522
+ }
523
+
524
+ .main-menu-dropdown .fas.fa-caret-down {
525
+ margin-left: 0.5vw;
526
+ font-size: 1vw;
527
+ transition: transform 0.2s;
528
+ }
529
+
530
+ .main-menu-dropdown:hover .fas.fa-caret-down,
531
+ .main-menu-dropdown:focus-within .fas.fa-caret-down {
532
+ transform: rotate(180deg);
533
+ }
534
+
535
+ .submenu {
536
+ display: none;
537
+ position: absolute;
538
+ top: 100%;
539
+ left: 50%;
540
+ transform: translateX(-50%);
541
+ min-width: 220px;
542
+ background: #fff;
543
+ box-shadow: 0 8px 24px rgba(0,0,0,0.12);
544
+ border-radius: 8px;
545
+ padding: 0.5vw 0;
546
+ z-index: 1100;
547
+ list-style: none;
548
+ white-space: nowrap;
549
+ }
550
+
551
+ .main-menu-dropdown:hover .submenu,
552
+ .main-menu-dropdown:focus-within .submenu {
553
+ display: block;
554
+ }
555
+
556
+ .submenu li a {
557
+ display: block;
558
+ padding: 0.7vw 1vw;
559
  color: #333;
560
+ font-size: 1.2vw;
561
+ font-weight: 600;
562
+ text-decoration: none;
563
+ border-radius: 6px;
564
+ transition: background 0.2s, color 0.2s;
565
  }
566
 
567
+ .submenu li a:hover,
568
+ .submenu li a.active-link {
569
+ background: #e6f2ff;
570
+ color: #137ec4;
571
  }
src/app/home/home.component.html CHANGED
@@ -1,29 +1,21 @@
 
1
  <div class="hero">
2
- <!-- Navigation Bar -->
3
  <nav class="navbar">
4
  <div class="navbar-container">
5
- <!-- Logo -->
6
  <div class="logo">
7
- <img src="assets/images/pykara-logo.png" alt="Logo" (click)="reloadPage()" />
8
  </div>
9
-
10
- <!-- Desktop Navigation Links -->
11
  <ul class="nav-links">
12
  <li><a routerLink="/chat" routerLinkActive="active-link">Chat</a></li>
13
  <li><a routerLink="/generate-questions" routerLinkActive="active-link">Grammar</a></li>
14
  <li>
15
- <!-- when disabled -->
16
  <span *ngIf="isVoiceDisabled; else voiceLink"
17
  class="nav-link nav-link--disabled"
18
  aria-disabled="true">
19
  Voice
20
  </span>
21
-
22
- <!-- when enabled -->
23
  <ng-template #voiceLink>
24
- <a class="nav-link"
25
- routerLink="/Voice"
26
- routerLinkActive="active-link">
27
  Voice
28
  </a>
29
  </ng-template>
@@ -33,13 +25,49 @@
33
  <li><a routerLink="/writing" routerLinkActive="active-link">Writing</a></li>
34
  <li><a routerLink="/vocabulary-builder" routerLinkActive="active-link">Vocabulary-Builder</a></li>
35
  <li><a routerLink="/findword" routerLinkActive="active-link">Find Word</a></li>
 
 
 
 
 
 
 
 
 
 
 
36
  </ul>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  </div>
38
  </nav>
39
 
40
- <!-- Hero Section -->
41
  <div class="hero-text">
42
- <h1>Py-Learn</h1>
43
  <h2>A Self-Learning System</h2>
44
  <p>
45
  It is a personalized self-learning system helps students to improve their language skills through interactive and customized exercises.
@@ -47,54 +75,1458 @@
47
  </p>
48
  </div>
49
 
50
- <!-- Cards Section -->
51
  <section class="cards-section">
52
  <h2 class="section-title">
53
  What would you like to <span class="highlight">learn</span> today?
54
  </h2>
55
-
56
  <div class="cards-container">
57
- <div class="card" *ngFor="let card of cards; let i = index">
58
  <div class="card-image">
59
  <img [src]="card.image" [alt]="card.title" />
60
  </div>
61
  <div class="card-content">
62
- <h3 class="card-title">{{ card.title }}</h3>
63
-
64
- <div #wrapper
65
- class="content-wrapper"
66
- [class.collapsed]="collapsedStates[i]"
67
- [class.expanded]="!collapsedStates[i]">
68
- <p>{{ card.description }}</p>
69
- </div>
70
-
71
  <div class="card-buttons">
72
- <button (click)="card.action()" class="card-button">Let's Start</button>
73
- <button *ngIf="isOverflowing[i]"
74
- (click)="toggleCard(i)"
75
- class="card-button">
76
- {{ collapsedStates[i] ? 'View More' : 'View Less' }}
77
- </button>
78
-
79
  </div>
80
  </div>
81
  </div>
82
-
83
-
84
-
85
  </div>
86
  </section>
87
 
88
- <!-- Footer -->
89
- <div class="footer">
90
- <p>© 2025 Pykara Technologies Pvt. Ltd. All rights reserved.</p>
91
- <div class="social-media">
92
- <a href="https://www.youtube.com/@PykaraTechnologies/videos" target="_blank">
93
- <img src="assets/images/home/youtube-icon.png" alt="YouTube">
 
 
 
 
 
 
 
 
 
94
  </a>
95
- <a href="https://www.youtube.com/@PykaraTechnologies/videos" target="_blank">
96
- <img src="assets/images/home/linkedin-icon.png" alt="LinkedIn">
 
 
 
97
  </a>
98
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
  </div>
100
  </div>
 
1
+ <!-- ================= NAVIGATION BAR ================= -->
2
  <div class="hero">
 
3
  <nav class="navbar">
4
  <div class="navbar-container">
 
5
  <div class="logo">
6
+ <img [src]="brand.logo" [alt]="brand.name + ' Logo'" (click)="reloadPage()" />
7
  </div>
 
 
8
  <ul class="nav-links">
9
  <li><a routerLink="/chat" routerLinkActive="active-link">Chat</a></li>
10
  <li><a routerLink="/generate-questions" routerLinkActive="active-link">Grammar</a></li>
11
  <li>
 
12
  <span *ngIf="isVoiceDisabled; else voiceLink"
13
  class="nav-link nav-link--disabled"
14
  aria-disabled="true">
15
  Voice
16
  </span>
 
 
17
  <ng-template #voiceLink>
18
+ <a class="nav-link" routerLink="/voice" routerLinkActive="active-link">
 
 
19
  Voice
20
  </a>
21
  </ng-template>
 
25
  <li><a routerLink="/writing" routerLinkActive="active-link">Writing</a></li>
26
  <li><a routerLink="/vocabulary-builder" routerLinkActive="active-link">Vocabulary-Builder</a></li>
27
  <li><a routerLink="/findword" routerLinkActive="active-link">Find Word</a></li>
28
+ <li class="main-menu-dropdown">
29
+ <a class="nav-link" href="#" (click)="$event.preventDefault()" aria-haspopup="true" aria-expanded="false">
30
+ General
31
+ <i class="fas fa-caret-down"></i>
32
+ </a>
33
+ <ul class="submenu">
34
+ <li><a routerLink="/pronunciation-improvement" routerLinkActive="active-link">Pronunciation Improvement</a></li>
35
+ <li><a routerLink="/personality-improvement" routerLinkActive="active-link">Personality Improvement</a></li>
36
+ <li><a routerLink="/body-language-improvement" routerLinkActive="active-link">Body Language Improvement</a></li>
37
+ </ul>
38
+ </li>
39
  </ul>
40
+ <!-- ================= ACCOUNT/LOGIN MENU ================= -->
41
+ <div class="nav-actions">
42
+ <ng-container *ngIf="isAuthenticated && username; else loginIcon">
43
+ <div class="avatar" (click)="toggleAccountMenu()" [attr.title]="usernameInitial">
44
+ {{ usernameInitial }}
45
+ </div>
46
+ <div class="account-menu" *ngIf="showAccountMenu" (mouseleave)="closeAccountMenu()">
47
+ <div class="account-header">
48
+ <div class="avatar avatar-lg">{{ usernameInitial }}</div>
49
+ <div class="account-meta">
50
+ <div class="account-name">{{ displayName }}</div>
51
+ <div class="account-email">{{ displayEmail }}</div>
52
+ </div>
53
+ </div>
54
+ <div class="account-item" (click)="goToAccount()">My account</div>
55
+ <hr />
56
+ <div class="account-item" (click)="logout()">Logout</div>
57
+ </div>
58
+ </ng-container>
59
+ <ng-template #loginIcon>
60
+ <a routerLink="/auth" class="login-button" [attr.title]="'Login'" aria-label="Login">
61
+ <i class="fas fa-user"></i>
62
+ </a>
63
+ </ng-template>
64
+ </div>
65
  </div>
66
  </nav>
67
 
68
+ <!-- ================= HERO SECTION ================= -->
69
  <div class="hero-text">
70
+ <h1>{{ brand.name }}</h1>
71
  <h2>A Self-Learning System</h2>
72
  <p>
73
  It is a personalized self-learning system helps students to improve their language skills through interactive and customized exercises.
 
75
  </p>
76
  </div>
77
 
78
+ <!-- ================= CARDS SECTION ================= -->
79
  <section class="cards-section">
80
  <h2 class="section-title">
81
  What would you like to <span class="highlight">learn</span> today?
82
  </h2>
 
83
  <div class="cards-container">
84
+ <div class="card" *ngFor="let card of cards; let i = index" (click)="card.action()" role="button" tabindex="0" (keydown.enter)="card.action()" (keydown.space)="card.action()">
85
  <div class="card-image">
86
  <img [src]="card.image" [alt]="card.title" />
87
  </div>
88
  <div class="card-content">
 
 
 
 
 
 
 
 
 
89
  <div class="card-buttons">
90
+ <button class="know-more-btn" (click)="$event.stopPropagation(); openGuidePopup(card.title)">Know More</button>
 
 
 
 
 
 
91
  </div>
92
  </div>
93
  </div>
 
 
 
94
  </div>
95
  </section>
96
 
97
+ <!-- ================= FOOTER ================= -->
98
+ <footer>
99
+ <p class="footer">© 2025 Pykara Technologies Pvt. Ltd. All rights reserved.</p>
100
+ <a href="https://pykara.ai/contact-us/" target="blank">Contact Us</a> |
101
+ <a href="#" (click)="openPrivacyPopup($event)">Privacy Policy</a> |
102
+ <a href="#" (click)="openTermsPopup($event)">Terms & Conditions</a>
103
+ <div class="social-icons">
104
+ <a href="https://www.linkedin.com/in/pykara-technologies" target="_blank" class="social-icon linkedin" aria-label="LinkedIn">
105
+ <i class="fab fa-linkedin-in"></i>
106
+ </a>
107
+ <a href="https://www.youtube.com/@PykaraTechnologies" target="_blank" class="social-icon youtube" aria-label="YouTube">
108
+ <i class="fab fa-youtube"></i>
109
+ </a>
110
+ <a href="https://www.facebook.com/people/Pykara/100087653675803/" target="_blank" class="social-icon facebook" aria-label="Facebook">
111
+ <i class="fab fa-facebook-f"></i>
112
  </a>
113
+ <a href="https://www.instagram.com/pykaratechnologie/" target="_blank" class="social-icon instagram" aria-label="Instagram">
114
+ <i class="fab fa-instagram"></i>
115
+ </a>
116
+ <a href="https://pykara.ai/" target="_blank" class="social-icon website" aria-label="Website">
117
+ <i class="fas fa-globe"></i>
118
  </a>
119
  </div>
120
+ </footer>
121
+
122
+ <!-- ================= GUIDE POPUP ================= -->
123
+ <div *ngIf="showGuidePopup">
124
+ <div class="user-guide-overlay" (click)="closeGuidePopup()"></div>
125
+ <div class="user-guide-modal" role="dialog" aria-modal="true">
126
+ <button class="user-guide-close-icon" (click)="closeGuidePopup()" aria-label="Close">&times;</button>
127
+ <ng-container [ngSwitch]="selectedCardTitle">
128
+ <!-- Card-specific guide content (all used) -->
129
+ <ng-container *ngSwitchCase="'Grammar Chat'">
130
+ <ul style="text-align: justify; font-size: 15px;">
131
+ <li>
132
+ <b>Introduction</b><br>
133
+ <ul style="list-style: unset; margin-left: 1.25rem;">
134
+ <li>The Chat Module is an interactive learning environment designed to assist learners through text and voice-based communication.</li>
135
+ <li>Users can type their question or select from follow-up suggestions, using either the text input or the microphone.</li>
136
+ <li>Predefined questions appear above the input field for quick access.</li>
137
+ <li>This creates an engaging and personalised learning experience similar to interacting with a real tutor.</li>
138
+ </ul>
139
+ </li>
140
+ <li>
141
+ <b>Starting the Chat</b><br>
142
+ <ul style="list-style: unset; margin-left: 1.25rem;">
143
+ <li>When learners open the module, they will see a text input box and a microphone icon. They can type a question or activate the microphone to speak.</li>
144
+ <li>While the microphone is active, a listening popup appears with options to mute or stop recording. Once the learner finishes speaking or typing, their question is displayed in the chat area along with a timestamp.</li>
145
+ <li>This simple interface ensures that both typing and speaking interactions are smooth and user-friendly.</li>
146
+ </ul>
147
+ </li>
148
+ <li>
149
+ <b>Accessing the Syllabus</b><br>
150
+ <ul style="list-style: unset; margin-left: 1.25rem;">
151
+ <li>Before learners begin, an administrator uploads the syllabus or textbook in digital format.</li>
152
+ <li>The system analyses the document and automatically generates a list of predefined questions based on the uploaded syllabus.</li>
153
+ <li>These predefined questions are displayed above the input field, allowing learners to choose any topic without needing to type.</li>
154
+ <li>When a learner selects a question, the system locates the relevant section from the syllabus and prepares an answer. The response appears instantly in the chat area in a clear and readable format.</li>
155
+ </ul>
156
+ </li>
157
+ <li>
158
+ <b>Receiving the Response</b><br>
159
+ <ul style="list-style: unset; margin-left: 1.25rem;">
160
+ <li>
161
+ After a question is sent, the system generates an immediate response that includes:
162
+ <ul>
163
+ <li>A text-based explanation</li>
164
+ <li>An audio narration in the tutor’s real voice</li>
165
+ <li>A derived video explanation, when applicable</li>
166
+ </ul>
167
+ </li>
168
+ <li>The response is first produced as text. If the learner chooses to listen, the system plays an audio narration that has been synthetically generated using the real voice of the teacher.</li>
169
+ <li>The voice is not a generic computer voice; it has been trained and modelled on the actual tutor’s speech patterns, ensuring that the tone, pronunciation, and expression closely resemble the teacher’s natural way of speaking.</li>
170
+ <li>Similarly, when a video explanation is requested, the system displays a derived video of the teacher. This video is not a pre-recorded clip or animation, but is generated to resemble the real teacher’s voice and reactions.</li>
171
+ <li>All audio and video responses are created dynamically for each question, providing unique, real-time explanations. Learners can replay or stop the narration at any time, copy text responses, and follow the conversation naturally with the speaking indicator showing when the tutor’s voice is active.</li>
172
+ <li>By default, audio is muted; you can enable it as needed.</li>
173
+ </ul>
174
+ </li>
175
+ <li>
176
+ <b>Handling Out-of-Syllabus Questions</b><br>
177
+ <ul style="list-style: unset; margin-left: 1.25rem;">
178
+ <li>If a learner asks a question that is not part of the uploaded syllabus or textbook, the system responds with the message: “This topic is out of syllabus.”</li>
179
+ <li>Only administrators can configure whether such questions can be answered using external information sources.</li>
180
+ <li>This ensures that all discussions remain within the approved syllabus unless authorised otherwise.</li>
181
+ </ul>
182
+ </li>
183
+ <li>
184
+ <b>Follow-Up and Progressive Learning</b><br>
185
+ <ul style="list-style: unset; margin-left: 1.25rem;">
186
+ <li>After each response, the system displays related or next-level questions below the chat. This feature helps learners progress through topics in a logical sequence.</li>
187
+ <li>A breadcrumb trail is also displayed, showing the topic flow and subtopics covered during the conversation.</li>
188
+ <li>Learners can easily revisit previous points and continue from where they left off.</li>
189
+ </ul>
190
+ </li>
191
+ <li>
192
+ <b>Audio, Video, and Mode Controls</b><br>
193
+ <ul style="list-style: unset; margin-left: 1.25rem;">
194
+ <li>
195
+ At the top of the chat interface, four control buttons provide flexibility and accessibility:
196
+ <ul>
197
+ <li>Audio Control – Enable or disable narration.</li>
198
+ <li>Video Control – Show or hide derived video explanations.</li>
199
+ <li>Syllabus Mode Control – Keep learning limited to syllabus topics.</li>
200
+ <li>Breadcrumb Control – Display or hide the topic trail.</li>
201
+ </ul>
202
+ </li>
203
+ <li>Only administrators can modify the syllabus mode to include out-of-syllabus responses.</li>
204
+ </ul>
205
+ </li>
206
+ <li>
207
+ <b>Interface and Usability</b><br>
208
+ <ul style="list-style: unset; margin-left: 1.25rem;">
209
+ <li>The chat interface presents a clear, conversational layout between the learner and the tutor. Each message includes a profile icon and timestamp for a natural reading flow.</li>
210
+ <li>Typing indicators appear while the system prepares responses, and a scroll button allows quick access to the most recent messages.</li>
211
+ <li>The design is responsive and adapts to different devices such as desktops, tablets, etc.</li>
212
+ </ul>
213
+ </li>
214
+ <li>
215
+ <b>Summary</b><br>
216
+ <ul style="list-style: unset; margin-left: 1.25rem;">
217
+ <li>The Chat Module provides an engaging, syllabus-focused learning experience where learners can type or speak their questions and receive immediate answers through text, real teacher voice, and derived video.</li>
218
+ <li>With predefined questions, real-time explanations, structured progression, and easy-to-use controls, this module offers a complete and intelligent conversational learning environment—all within a single platform.</li>
219
+ <li>Use this feature for summary-guided training.</li>
220
+ </ul>
221
+ </li>
222
+ </ul>
223
+ </ng-container>
224
+
225
+ <!-- Grammar Quiz specific content -->
226
+ <ng-container *ngSwitchCase="'Grammar Quiz'">
227
+ <ul style="text-align: justify; font-size: 15px;">
228
+ <li>
229
+ <b>Introduction</b><br>
230
+ <ul style="list-style: unset; margin-left: 1.25rem;">
231
+ <li>The <b>Generate Questions</b> module helps children practise English grammar with step-by-step questions, instant feedback, and level-based progression.</li>
232
+ <li>All questions, hints, and follow-ups are drawn from a <b>predefined syllabus</b> prepared by your school or administrator.</li>
233
+ <li>The layout is simple and child-friendly, with clear buttons and readable text.</li>
234
+ </ul>
235
+ </li>
236
+ <li>
237
+ <b>Syllabus Source</b><br>
238
+ <ul style="list-style: unset; margin-left: 1.25rem;">
239
+ <li>The module uses a <b>predefined syllabus</b> that includes topics, chapters, sections, and pages.</li>
240
+ <li>Predefined content ensures that practice stays aligned with the lessons taught in class.</li>
241
+ <li>If a topic is not found in the syllabus, the system displays a clear message (see “Out-of-Syllabus Control”).</li>
242
+ </ul>
243
+ </li>
244
+ <li>
245
+ <b>Choose a Topic</b><br>
246
+ <ul style="list-style: unset; margin-left: 1.25rem;">
247
+ <li>Type a grammar topic (e.g., <i>verbs</i>, <i>nouns</i>, <i>tenses</i>, <i>subject–verb agreement</i>) or select from the suggestion list.</li>
248
+ <li>Use specific terms (e.g., <i>simple past tense</i>) for better results.</li>
249
+ <li>If the topic is unclear or unrelated to grammar, the system may show: “Please enter a valid <b>grammar topic</b>, not a general word or unrelated question.”</li>
250
+ </ul>
251
+ </li>
252
+ <li>
253
+ <b>Generate Questions</b><br>
254
+ <ul style="list-style: unset; margin-left: 1.25rem;">
255
+ <li>Click <b>Generate</b> to create a set of questions based on the chosen topic from the predefined syllabus.</li>
256
+ <li>A brief loading screen appears while the questions are prepared.</li>
257
+ <li>To keep the practice consistent, the topic may be locked after generation until you reset the session.</li>
258
+ </ul>
259
+ </li>
260
+ <li>
261
+ <b>Answer the Questions</b><br>
262
+ <ul style="list-style: unset; margin-left: 1.25rem;">
263
+ <li>Each <b>question</b> presents a sentence with a <b>blank</b>. The learner may type any suitable <b>answer</b> in the blank.</li>
264
+ <li>All blanks must be filled before you can check answers. The <b>Validate/Check Answers</b> button enables automatically.</li>
265
+ <li>The input fields are designed for easy typing and clear visibility.</li>
266
+ </ul>
267
+ </li>
268
+ <li>
269
+ <b>Check Your Answers</b><br>
270
+ <ul style="list-style: unset; margin-left: 1.25rem;">
271
+ <li>Click <b>Check Answers</b> to validate all responses together.</li>
272
+ <li><b>Color guidance:</b> correct answers are shown in green with the label <b>Correct</b>; incorrect answers are shown in red with the label <b>Wrong</b>.</li>
273
+ <li>During validation or timed messages, inputs may be temporarily read-only to prevent accidental edits.</li>
274
+ </ul>
275
+ </li>
276
+ <li>
277
+ <b>Try Again (First Wrong Attempt)</b><br>
278
+ <ul style="list-style: unset; margin-left: 1.25rem;">
279
+ <li>For each incorrect <b>answer</b>, a <b>“Try Again”</b> message appears with a short countdown.</li>
280
+ <li>After the countdown, the wrong entry is cleared so the learner can attempt again.</li>
281
+ <li>This encourages self-correction with gentle guidance.</li>
282
+ </ul>
283
+ </li>
284
+ <li>
285
+ <b>Show Correct Answer (Second Wrong Attempt)</b><br>
286
+ <ul style="list-style: unset; margin-left: 1.25rem;">
287
+ <li>If a <b>question</b> remains incorrect after the second attempt, another countdown appears.</li>
288
+ <li>When the countdown ends, the <b>correct answer</b> is shown and filled automatically.</li>
289
+ <li>This provides closure and allows the learner to continue smoothly.</li>
290
+ </ul>
291
+ </li>
292
+ <li>
293
+ <b>Hints and Teacher Coaching</b><br>
294
+ <ul style="list-style: unset; margin-left: 1.25rem;">
295
+ <li>Click the <b>Hint</b> icon to open helpful hints based on the same syllabus section as the questions.</li>
296
+ <li>When a hint is displayed or when an answer is incorrect, the module can also present the teacher’s voice and a short teacher video that explains the idea in simple words.</li>
297
+ <li>Audio is muted by default; you may enable it. You can play, pause, or close the video at any time.</li>
298
+ </ul>
299
+ </li>
300
+ <li>
301
+ <b>Breadcrumb and Follow-Up Questions</b><br>
302
+ <ul style="list-style: unset; margin-left: 1.25rem;">
303
+ <li>The <b>breadcrumb</b> shows the learning path (Topic ? Chapter ? Section ? Page). Clicking a level focuses the practice on that part.</li>
304
+ <li><b>Predefined follow-up questions</b> appear below the main set. They come from the same syllabus section and guide the next step (e.g., identification, transformation, short explanation).</li>
305
+ <li>As answers are submitted, the follow-ups adjust to reinforce areas that need more practice.</li>
306
+ </ul>
307
+ </li>
308
+ <li>
309
+ <b>Out-of-Syllabus Control</b><br>
310
+ <ul style="list-style: unset; margin-left: 1.25rem;">
311
+ <li>If a learner asks about a topic that is not part of the predefined syllabus, the system shows: “This topic is out of syllabus.”</li>
312
+ <li>Only administrators can decide whether such questions may be answered using other sources.</li>
313
+ <li>This keeps practice within approved lessons unless specific permission is given.</li>
314
+ </ul>
315
+ </li>
316
+ <li>
317
+ <b>Level Progress and Progress Bar</b><br>
318
+ <ul style="list-style: unset; margin-left: 1.25rem;">
319
+ <li>The module uses three levels: <b>Basic ? Intermediate ? Expert</b>.</li>
320
+ <li>A <b>progress bar</b> with level markers shows the current level and the remaining path.</li>
321
+ <li>Clear labels and gentle animations help children track progress.</li>
322
+ </ul>
323
+ </li>
324
+ <li>
325
+ <b>Moving to the Next Level</b><br>
326
+ <ul style="list-style: unset; margin-left: 1.25rem;">
327
+ <li>When all <b>questions</b> in a level are correct, a message and a short countdown appear.</li>
328
+ <li>The module then advances to the next level and updates the progress bar.</li>
329
+ <li>Difficulty increases gradually to build mastery step by step.</li>
330
+ </ul>
331
+ </li>
332
+ <li>
333
+ <b>Celebrate Success</b><br>
334
+ <ul style="list-style: unset; margin-left: 1.25rem;">
335
+ <li>After completing all three levels, a <b>celebration</b> screen appears.</li>
336
+ <li>Learners can click <b>Start Over</b> to practise the same topic again or choose a new topic.</li>
337
+ </ul>
338
+ </li>
339
+ <li>
340
+ <b>Reset or Change Topic</b><br>
341
+ <ul style="list-style: unset; margin-left: 1.25rem;">
342
+ <li>Use <b>Reset</b> to clear the current session and unlock the topic field.</li>
343
+ <li>Enter a new topic or select another suggestion to begin a new practice set.</li>
344
+ </ul>
345
+ </li>
346
+ <li>
347
+ <b>Accessibility and Layout</b><br>
348
+ <ul style="list-style: unset; margin-left: 1.25rem;">
349
+ <li>Countdowns and status messages are written clearly so learners understand what is happening.</li>
350
+ <li>The layout adapts to different screens. Buttons and inputs are sized for comfortable tapping and clicking.</li>
351
+ </ul>
352
+ </li>
353
+ <li>
354
+ <b>Troubleshooting</b><br>
355
+ <ul style="list-style: unset; margin-left: 1.25rem;">
356
+ <li><b>Invalid topic message:</b> Enter a clear grammar term (e.g., <i>articles</i>, <i>comparatives</i>, <i>passive voice</i>).</li>
357
+ <li><b>Out of syllabus:</b> Choose a topic that appears in the syllabus list or ask an administrator about permission for extra topics.</li>
358
+ <li><b>Buttons disabled:</b> The <i>Generate</i> button appears after entering a valid topic; the <i>Validate</i> button appears after all blanks are filled.</li>
359
+ </ul>
360
+ </li>
361
+ <li>
362
+ <b>Benefits for Children</b><br>
363
+ <ul style="list-style: unset; margin-left: 1.25rem;">
364
+ <li><b>Guided practice</b> with clear feedback builds confidence and accuracy.</li>
365
+ <li><b>Level-based progression</b> supports steady growth from basic understanding to higher skills.</li>
366
+ <li><b>Hints and teacher coaching</b> help learners correct mistakes and understand rules in simple language.</li>
367
+ <li><b>Predefined syllabus alignment</b> keeps practice consistent with classroom lessons.</li>
368
+ </ul>
369
+ </li>
370
+ </ul>
371
+ </ng-container>
372
+
373
+ <!-- Voice specific content -->
374
+ <ng-container *ngSwitchCase="'Voice'">
375
+ <ul style="text-align: justify; font-size: 15px;">
376
+ <li>
377
+ <b>Introduction</b><br>
378
+ <ul style="list-style: unset; margin-left: 1.25rem;">
379
+ <li>The <b>voice </b> module helps children practise English grammar with step-by-step questions, instant feedback, and level-based progression.</li>
380
+ <li>All questions, hints, and follow-ups are drawn from a <b>predefined syllabus</b> prepared by your school or administrator.</li>
381
+ <li>The layout is simple and child-friendly, with clear buttons and readable text.</li>
382
+ </ul>
383
+ </li>
384
+ <li>
385
+ <b>Syllabus Source</b><br>
386
+ <ul style="list-style: unset; margin-left: 1.25rem;">
387
+ <li>The module uses a <b>predefined syllabus</b> that includes topics, chapters, sections, and pages.</li>
388
+ <li>Predefined content ensures that practice stays aligned with the lessons taught in class.</li>
389
+ <li>If a topic is not found in the syllabus, the system displays a clear message (see “Out-of-Syllabus Control”).</li>
390
+ </ul>
391
+ </li>
392
+ <li>
393
+ <b>Choose a Topic</b><br>
394
+ <ul style="list-style: unset; margin-left: 1.25rem;">
395
+ <li>Type a grammar topic (e.g., <i>verbs</i>, <i>nouns</i>, <i>tenses</i>, <i>subject–verb agreement</i>) or select from the suggestion list.</li>
396
+ <li>Use specific terms (e.g., <i>simple past tense</i>) for better results.</li>
397
+ <li>If the topic is unclear or unrelated to grammar, the system may show: “Please enter a valid <b>grammar topic</b>, not a general word or unrelated question.”</li>
398
+ </ul>
399
+ </li>
400
+ <li>
401
+ <b>Generate Questions</b><br>
402
+ <ul style="list-style: unset; margin-left: 1.25rem;">
403
+ <li>Click <b>Generate</b> to create a set of questions based on the chosen topic from the predefined syllabus.</li>
404
+ <li>A brief loading screen appears while the questions are prepared.</li>
405
+ <li>To keep the practice consistent, the topic may be locked after generation until you reset the session.</li>
406
+ </ul>
407
+ </li>
408
+ <li>
409
+ <b>Answer the Questions</b><br>
410
+ <ul style="list-style: unset; margin-left: 1.25rem;">
411
+ <li>Each <b>question</b> presents a sentence with a <b>blank</b>. The learner may type any suitable <b>answer</b> in the blank.</li>
412
+ <li>All blanks must be filled before you can check answers. The <b>Validate/Check Answers</b> button enables automatically.</li>
413
+ <li>The input fields are designed for easy typing and clear visibility.</li>
414
+ </ul>
415
+ </li>
416
+ <li>
417
+ <b>Check Your Answers</b><br>
418
+ <ul style="list-style: unset; margin-left: 1.25rem;">
419
+ <li>Click <b>Check Answers</b> to validate all responses together.</li>
420
+ <li><b>Color guidance:</b> correct answers are shown in green with the label <b>Correct</b>; incorrect answers are shown in red with the label <b>Wrong</b>.</li>
421
+ <li>During validation or timed messages, inputs may be temporarily read-only to prevent accidental edits.</li>
422
+ </ul>
423
+ </li>
424
+ <li>
425
+ <b>Try Again (First Wrong Attempt)</b><br>
426
+ <ul style="list-style: unset; margin-left: 1.25rem;">
427
+ <li>For each incorrect <b>answer</b>, a <b>“Try Again”</b> message appears with a short countdown.</li>
428
+ <li>After the countdown, the wrong entry is cleared so the learner can attempt again.</li>
429
+ <li>This encourages self-correction with gentle guidance.</li>
430
+ </ul>
431
+ </li>
432
+ <li>
433
+ <b>Show Correct Answer (Second Wrong Attempt)</b><br>
434
+ <ul style="list-style: unset; margin-left: 1.25rem;">
435
+ <li>If a <b>question</b> remains incorrect after the second attempt, another countdown appears.</li>
436
+ <li>When the countdown ends, the <b>correct answer</b> is shown and filled automatically.</li>
437
+ <li>This provides closure and allows the learner to continue smoothly.</li>
438
+ </ul>
439
+ </li>
440
+ <li>
441
+ <b>Hints and Teacher Coaching</b><br>
442
+ <ul style="list-style: unset; margin-left: 1.25rem;">
443
+ <li>Click the <b>Hint</b> icon to open helpful hints based on the same syllabus section as the questions.</li>
444
+ <li>When a hint is displayed or when an answer is incorrect, the module can also present the teacher’s voice and a short teacher video that explains the idea in simple words.</li>
445
+ <li>Audio is muted by default; you may enable it. You can play, pause, or close the video at any time.</li>
446
+ </ul>
447
+ </li>
448
+ <li>
449
+ <b>Breadcrumb and Follow-Up Questions</b><br>
450
+ <ul style="list-style: unset; margin-left: 1.25rem;">
451
+ <li>The <b>breadcrumb</b> shows the learning path (Topic ? Chapter ? Section ? Page). Clicking a level focuses the practice on that part.</li>
452
+ <li><b>Predefined follow-up questions</b> appear below the main set. They come from the same syllabus section and guide the next step (e.g., identification, transformation, short explanation).</li>
453
+ <li>As answers are submitted, the follow-ups adjust to reinforce areas that need more practice.</li>
454
+ </ul>
455
+ </li>
456
+ <li>
457
+ <b>Out-of-Syllabus Control</b><br>
458
+ <ul style="list-style: unset; margin-left: 1.25rem;">
459
+ <li>If a learner asks about a topic that is not part of the predefined syllabus, the system shows: “This topic is out of syllabus.”</li>
460
+ <li>Only administrators can decide whether such questions may be answered using other sources.</li>
461
+ <li>This keeps practice within approved lessons unless specific permission is given.</li>
462
+ </ul>
463
+ </li>
464
+ <li>
465
+ <b>Level Progress and Progress Bar</b><br>
466
+ <ul style="list-style: unset; margin-left: 1.25rem;">
467
+ <li>The module uses three levels: <b>Basic ? Intermediate ? Expert</b>.</li>
468
+ <li>A <b>progress bar</b> with level markers shows the current level and the remaining path.</li>
469
+ <li>Clear labels and gentle animations help children track progress.</li>
470
+ </ul>
471
+ </li>
472
+ <li>
473
+ <b>Moving to the Next Level</b><br>
474
+ <ul style="list-style: unset; margin-left: 1.25rem;">
475
+ <li>When all <b>questions</b> in a level are correct, a message and a short countdown appear.</li>
476
+ <li>The module then advances to the next level and updates the progress bar.</li>
477
+ <li>Difficulty increases gradually to build mastery step by step.</li>
478
+ </ul>
479
+ </li>
480
+ <li>
481
+ <b>Celebrate Success</b><br>
482
+ <ul style="list-style: unset; margin-left: 1.25rem;">
483
+ <li>After completing all three levels, a <b>celebration</b> screen appears.</li>
484
+ <li>Learners can click <b>Start Over</b> to practise the same topic again or choose a new topic.</li>
485
+ </ul>
486
+ </li>
487
+ <li>
488
+ <b>Reset or Change Topic</b><br>
489
+ <ul style="list-style: unset; margin-left: 1.25rem;">
490
+ <li>Use <b>Reset</b> to clear the current session and unlock the topic field.</li>
491
+ <li>Enter a new topic or select another suggestion to begin a new practice set.</li>
492
+ </ul>
493
+ </li>
494
+ <li>
495
+ <b>Accessibility and Layout</b><br>
496
+ <ul style="list-style: unset; margin-left: 1.25rem;">
497
+ <li>Countdowns and status messages are written clearly so learners understand what is happening.</li>
498
+ <li>The layout adapts to different screens. Buttons and inputs are sized for comfortable tapping and clicking.</li>
499
+ </ul>
500
+ </li>
501
+ <li>
502
+ <b>Troubleshooting</b><br>
503
+ <ul style="list-style: unset; margin-left: 1.25rem;">
504
+ <li><b>Invalid topic message:</b> Enter a clear grammar term (e.g., <i>articles</i>, <i>comparatives</i>, <i>passive voice</i>).</li>
505
+ <li><b>Out of syllabus:</b> Choose a topic that appears in the syllabus list or ask an administrator about permission for extra topics.</li>
506
+ <li><b>Buttons disabled:</b> The <i>Generate</i> button appears after entering a valid topic; the <i>Validate</i> button appears after all blanks are filled.</li>
507
+ </ul>
508
+ </li>
509
+ <li>
510
+ <b>Benefits for Children</b><br>
511
+ <ul style="list-style: unset; margin-left: 1.25rem;">
512
+ <li><b>Guided practice</b> with clear feedback builds confidence and accuracy.</li>
513
+ <li><b>Level-based progression</b> supports steady growth from basic understanding to higher skills.</li>
514
+ <li><b>Hints and teacher coaching</b> help learners correct mistakes and understand rules in simple language.</li>
515
+ <li><b>Predefined syllabus alignment</b> keeps practice consistent with classroom lessons.</li>
516
+ </ul>
517
+ </li>
518
+ </ul>
519
+ </ng-container>
520
+
521
+ <!-- Listening specific content -->
522
+ <ng-container *ngSwitchCase="'Listening'">
523
+ <ul style="text-align: justify; font-size: 15px;">
524
+ <li>
525
+ <b>Introduction</b><br>
526
+ <ul style="list-style: unset; margin-left: 1.25rem;">
527
+ <li>The Listen module helps learners improve listening skills using short videos.</li>
528
+ <li>It provides questions based on the video and gives instant feedback.</li>
529
+ <li>It supports repeated attempts, so learners can review, retry, and improve.</li>
530
+ </ul>
531
+ </li>
532
+
533
+ <li>
534
+ <b>Preparing Your Video</b><br>
535
+ <ul style="list-style: unset; margin-left: 1.25rem;">
536
+ <li>Upload a video from your device or select one from the list (if available).</li>
537
+ <li>Check your audio device (headphones/speakers) before you start.</li>
538
+ <li>Use a quiet environment for better focus.</li>
539
+ </ul>
540
+ </li>
541
+
542
+ <li>
543
+ <b>Playing and Enabling Questions</b><br>
544
+ <ul style="list-style: unset; margin-left: 1.25rem;">
545
+ <li>Press <b>Play</b> and watch the video from start to finish.</li>
546
+ <li>After you complete the video, the <b>Generate Questions</b> button becomes active.</li>
547
+ <li>You may replay parts of the video before generating questions if needed.</li>
548
+ </ul>
549
+ </li>
550
+
551
+ <li>
552
+ <b>Generating and Answering Questions</b><br>
553
+ <ul style="list-style: unset; margin-left: 1.25rem;">
554
+ <li>Click <b>Generate Questions</b> to create questions based on the video content.</li>
555
+ <li>Question types include multiple choice, fill-in-the-blank, short answer, and dictation.</li>
556
+ <li>Answer each question carefully; you can move between questions if navigation is enabled.</li>
557
+ </ul>
558
+ </li>
559
+
560
+ <li>
561
+ <b>Validation and Feedback</b><br>
562
+ <ul style="list-style: unset; margin-left: 1.25rem;">
563
+ <li>Click <b>Validate Answers</b> to check your responses.</li>
564
+ <li>You will see which answers are correct and which need revision.</li>
565
+ <li>Incorrect items can be retried; use the video or hints to correct your answers.</li>
566
+ </ul>
567
+ </li>
568
+
569
+ <li>
570
+ <b>Hints and Transcript</b><br>
571
+ <ul style="list-style: unset; margin-left: 1.25rem;">
572
+ <li>Hints may give a short clue, a key phrase, or a time range to review.</li>
573
+ <li>A transcript may be available after your first attempt, depending on settings.</li>
574
+ <li>Use these aids to understand difficult sections before retrying.</li>
575
+ </ul>
576
+ </li>
577
+
578
+ <li>
579
+ <b>Playback Controls</b><br>
580
+ <ul style="list-style: unset; margin-left: 1.25rem;">
581
+ <li>Use <b>replay</b> to review important parts and <b>seek</b> to jump to a time.</li>
582
+ <li>Adjust <b>playback speed</b> to slow down or speed up the audio.</li>
583
+ <li>Audio may start muted by default; enable sound when ready.</li>
584
+ </ul>
585
+ </li>
586
+
587
+ <li>
588
+ <b>Progress and Scoring</b><br>
589
+ <ul style="list-style: unset; margin-left: 1.25rem;">
590
+ <li>Your attempts and scores are recorded for review.</li>
591
+ <li>You can retry incorrect questions to improve your score.</li>
592
+ <li>Use your history to track improvement over time.</li>
593
+ </ul>
594
+ </li>
595
+
596
+ <li>
597
+ <b>Good Practice</b><br>
598
+ <ul style="list-style: unset; margin-left: 1.25rem;">
599
+ <li>Use headphones for clear audio and fewer distractions.</li>
600
+ <li>Pause and replay difficult parts before answering.</li>
601
+ <li>Focus on key words, speaker tone, and context.</li>
602
+ </ul>
603
+ </li>
604
+
605
+ <li>
606
+ <b>Summary</b><br>
607
+ <ul style="list-style: unset; margin-left: 1.25rem;">
608
+ <li>The Listen module provides video-based practice with questions and instant feedback.</li>
609
+ <li>Replay, hints, and transcripts support understanding and correction.</li>
610
+ <li>Use it regularly to build confident and accurate listening skills.</li>
611
+ </ul>
612
+ </li>
613
+ </ul>
614
+
615
+ </ng-container>
616
+
617
+ <!--Reading-->
618
+ <!--Reading-->
619
+ <ng-container *ngSwitchCase="'Reading'">
620
+ <ul style="text-align: justify; font-size: 15px;">
621
+ <li>
622
+ <b>Introduction</b><br>
623
+ <ul style="list-style: unset; margin-left: 1.25rem;">
624
+ <li>
625
+ The <b>Reading</b> module is a guided reading-comprehension exercise where learners
626
+ enter a topic, get a short passage at a chosen difficulty level, and then answer
627
+ questions based on that passage.
628
+ </li>
629
+ <li>
630
+ It is designed as a self-learning tool for children, with simple controls, large
631
+ buttons, and clear visual feedback.
632
+ </li>
633
+ <li>
634
+ The module can be used in class, in labs, or at home to give structured,
635
+ independent reading practice.
636
+ </li>
637
+ </ul>
638
+ </li>
639
+
640
+ <li>
641
+ <b>Selecting the Difficulty Level</b><br>
642
+ <ul style="list-style: unset; margin-left: 1.25rem;">
643
+ <li>
644
+ Before typing any topic, the learner must choose a <b>difficulty level</b>:
645
+ <b>Easy</b>, <b>Medium</b>, or <b>Hard</b>.
646
+ </li>
647
+ <li>
648
+ The topic box remains locked until a level is selected, so children follow the
649
+ steps in the right order and do not get confused.
650
+ </li>
651
+ <li>
652
+ Teachers can guide younger students to use Easy level, while older or stronger
653
+ readers can use Medium or Hard passages for more challenge.
654
+ </li>
655
+ </ul>
656
+ </li>
657
+
658
+ <li>
659
+ <b>Entering the Topic and Generating the Passage</b><br>
660
+ <ul style="list-style: unset; margin-left: 1.25rem;">
661
+ <li>
662
+ After selecting the level, the learner types a topic (for example:
663
+ <i>Rainy Day</i>, <i>Animals</i>, <i>My School</i>, <i>Space</i>).
664
+ </li>
665
+ <li>
666
+ The system checks that the topic is meaningful and safe. If it is not valid,
667
+ a clear message is shown so the learner can correct it.
668
+ </li>
669
+ <li>
670
+ When the topic is valid, clicking <b>Generate Passage</b> creates a short,
671
+ level-appropriate reading passage based on that topic.
672
+ </li>
673
+ <li>
674
+ A loading indicator is shown while the passage is being generated so learners
675
+ know the system is working.
676
+ </li>
677
+ </ul>
678
+ </li>
679
+
680
+ <li>
681
+ <b>Reading the Passage</b><br>
682
+ <ul style="list-style: unset; margin-left: 1.25rem;">
683
+ <li>
684
+ The passage is displayed in a dedicated reading card, with neat spacing and
685
+ comfortable line height for children.
686
+ </li>
687
+ <li>
688
+ Learners can adjust the <b>font size</b> using A− / A+ buttons so the text is
689
+ easy to see on different devices.
690
+ </li>
691
+ <li>
692
+ A <b>Read Aloud</b> button lets the passage be read using audio. Children can
693
+ start, pause, or resume listening as needed.
694
+ </li>
695
+ <li>
696
+ These options help visual and auditory learners, and support children who need
697
+ help with pronunciation or slower reading.
698
+ </li>
699
+ </ul>
700
+ </li>
701
+
702
+ <li>
703
+ <b>Generating Questions from the Passage</b><br>
704
+ <ul style="list-style: unset; margin-left: 1.25rem;">
705
+ <li>
706
+ Once the learner has read or listened to the passage, they click
707
+ <b>Generate Questions</b>.
708
+ </li>
709
+ <li>
710
+ The system creates a set of <b>multiple-choice questions</b> directly linked
711
+ to the passage so that all questions stay within the same context.
712
+ </li>
713
+ <li>
714
+ A small loader or waiting message appears during question generation to prevent
715
+ repeated clicks and to show progress.
716
+ </li>
717
+ </ul>
718
+ </li>
719
+
720
+ <li>
721
+ <b>Answering and Validating Questions</b><br>
722
+ <ul style="list-style: unset; margin-left: 1.25rem;">
723
+ <li>
724
+ Questions are presented <b>one at a time</b>, each with four options.
725
+ Answer choices are shown as clear radio buttons with child-friendly layout.
726
+ </li>
727
+ <li>
728
+ The learner first selects an option. The selected option is highlighted so the
729
+ child clearly sees their current choice before checking.
730
+ </li>
731
+ <li>
732
+ After choosing an option, the learner clicks <b>Validate</b>:
733
+ <ul style="list-style: disc; margin-left: 1.4rem;">
734
+ <li>Correct answers are shown with a friendly success style (e.g. green with a tick).</li>
735
+ <li>Wrong answers are shown with a clear but gentle error style (e.g. red with a cross).</li>
736
+ </ul>
737
+ </li>
738
+ <li>
739
+ The Validate button is disabled after use for that question, preventing
740
+ repeated clicks and keeping the flow simple.
741
+ </li>
742
+ <li>
743
+ The button label then changes to allow the learner to move to the
744
+ <b>Next</b> question. On the final question it may change to a reset or
745
+ completion action.
746
+ </li>
747
+ </ul>
748
+ </li>
749
+
750
+ <li>
751
+ <b>Completion, Score, and Restart</b><br>
752
+ <ul style="list-style: unset; margin-left: 1.25rem;">
753
+ <li>
754
+ When all questions are completed, the module shows a <b>summary</b> with the
755
+ learner’s score (for example, “You answered 4 out of 5 questions correctly”).
756
+ </li>
757
+ <li>
758
+ A simple congratulations or encouragement message is shown to keep motivation high.
759
+ </li>
760
+ <li>
761
+ Learners can then click <b>Start Over</b> or a similar button to clear the
762
+ current session, choose a new topic, or change the level and begin a fresh
763
+ reading exercise.
764
+ </li>
765
+ </ul>
766
+ </li>
767
+
768
+ <li>
769
+ <b>Benefits for Children</b><br>
770
+ <ul style="list-style: unset; margin-left: 1.25rem;">
771
+ <li>
772
+ Builds <b>reading comprehension</b> by connecting text with follow-up questions.
773
+ </li>
774
+ <li>
775
+ Improves <b>vocabulary</b> naturally through level-based passages on familiar topics.
776
+ </li>
777
+ <li>
778
+ Encourages <b>focus and attention</b> by showing only one question at a time.
779
+ </li>
780
+ <li>
781
+ Provides <b>instant feedback</b> so learners understand their mistakes and learn immediately.
782
+ </li>
783
+ <li>
784
+ Supports different learning needs with font size controls and read-aloud options.
785
+ </li>
786
+ <li>
787
+ Reduces teacher workload, because passages and questions are generated automatically
788
+ instead of being prepared manually for each topic.
789
+ </li>
790
+ </ul>
791
+ </li>
792
+ </ul>
793
+ </ng-container>
794
+
795
+
796
+ <!--Writing-->
797
+ <!-- Writing -->
798
+ <ng-container *ngSwitchCase="'Writing'">
799
+ <ul style="text-align: justify; font-size: 15px;">
800
+ <li>
801
+ <b>Introduction</b><br>
802
+ <ul style="list-style: unset; margin-left: 1.25rem;">
803
+ <li>
804
+ The <b>Writing</b> module gives students a structured way to practise
805
+ writing in English. It provides a topic based on the selected grade
806
+ level, lets students write their answer, and then shows clear
807
+ suggestions for improvement.
808
+ </li>
809
+ <li>
810
+ The activity is designed for children to use at school or at home,
811
+ with a simple layout, friendly images, and clear buttons that guide
812
+ them step by step.
813
+ </li>
814
+ <li>
815
+ It helps students develop better sentence structure, grammar, and
816
+ expression through regular practice.
817
+ </li>
818
+ </ul>
819
+ </li>
820
+
821
+ <li>
822
+ <b>Selecting the Grade and Getting a Topic</b><br>
823
+ <ul style="list-style: unset; margin-left: 1.25rem;">
824
+ <li>
825
+ Students first choose a <b>grade band</b> from the dropdown:
826
+ Lower, Middle, or Upper.
827
+ </li>
828
+ <li>
829
+ After selecting the grade, they click <b>Get Topic</b>. The system
830
+ then provides a suitable topic for that level, such as a simple daily
831
+ life topic for lower grades or a slightly more thoughtful topic for
832
+ higher grades.
833
+ </li>
834
+ <li>
835
+ A small loader is shown while the topic is being fetched, so students
836
+ understand that the system is working and do not keep clicking.
837
+ </li>
838
+ </ul>
839
+ </li>
840
+
841
+ <li>
842
+ <b>Writing on the Given Topic</b><br>
843
+ <ul style="list-style: unset; margin-left: 1.25rem;">
844
+ <li>
845
+ Once the topic is loaded, it is displayed inside a highlighted topic
846
+ box so students always see what they need to write about.
847
+ </li>
848
+ <li>
849
+ A large, notebook-style <b>writing box</b> is provided where students
850
+ type their answer in their own words.
851
+ </li>
852
+ <li>
853
+ The text area is big enough for a short paragraph and is easy to use
854
+ on both laptops and tablets.
855
+ </li>
856
+ </ul>
857
+ </li>
858
+
859
+ <li>
860
+ <b>Minimum Word Requirement and Submit Button</b><br>
861
+ <ul style="list-style: unset; margin-left: 1.25rem;">
862
+ <li>
863
+ To encourage meaningful writing, the submit button remains disabled
864
+ until the student has written at least <b>10 words</b>.
865
+ </li>
866
+ <li>
867
+ When the button is disabled, a small blue tooltip appears explaining
868
+ that “We need at least 10 words to enable the submit button.”
869
+ </li>
870
+ <li>
871
+ After the minimum length is reached, the <b>Submit Writing</b> button
872
+ becomes active. On click, the text is sent for checking and the
873
+ button text changes to “Submitting...” while the response is being
874
+ processed.
875
+ </li>
876
+ </ul>
877
+ </li>
878
+
879
+ <li>
880
+ <b>Feedback and Suggestions for Improvement</b><br>
881
+ <ul style="list-style: unset; margin-left: 1.25rem;">
882
+ <li>
883
+ After submission, students see a <b>Suggestions for Improvement</b>
884
+ section.
885
+ </li>
886
+ <li>
887
+ Feedback is shown as a list of clear, short points, such as
888
+ suggestions about grammar, sentence formation, or clarity.
889
+ </li>
890
+ <li>
891
+ This helps students understand what they did well and what they can
892
+ improve in their writing next time.
893
+ </li>
894
+ </ul>
895
+ </li>
896
+
897
+ <li>
898
+ <b>Try Another Topic</b><br>
899
+ <ul style="list-style: unset; margin-left: 1.25rem;">
900
+ <li>
901
+ A <b>Try Another</b> button allows students to reset the activity,
902
+ get a new topic, and practise again.
903
+ </li>
904
+ <li>
905
+ This makes the module suitable for regular writing practice, such as
906
+ weekly writing tasks or extra homework.
907
+ </li>
908
+ </ul>
909
+ </li>
910
+
911
+ <li>
912
+ <b>Benefits for Students</b><br>
913
+ <ul style="list-style: unset; margin-left: 1.25rem;">
914
+ <li>
915
+ Develops regular <b>writing habit</b> and improves confidence in
916
+ expressing ideas in English.
917
+ </li>
918
+ <li>
919
+ Strengthens <b>grammar, vocabulary, and sentence structure</b>
920
+ through repeated practice.
921
+ </li>
922
+ <li>
923
+ Provides <b>instant, clear feedback</b> instead of only marks, so
924
+ students know what to improve.
925
+ </li>
926
+ <li>
927
+ Reduces teacher workload by automating topic generation and basic
928
+ feedback, while still keeping the activity meaningful for students.
929
+ </li>
930
+ </ul>
931
+ </li>
932
+ </ul>
933
+ </ng-container>
934
+
935
+
936
+ <!--Vocabulary Builder-->
937
+ <!-- Vocabulary Builder -->
938
+ <ng-container *ngSwitchCase="'Vocabulary Builder'">
939
+ <ul style="text-align: justify; font-size: 15px;">
940
+ <li>
941
+ <b>Introduction</b><br>
942
+ <ul style="list-style: unset; margin-left: 1.25rem;">
943
+ <li>
944
+ The <b>Vocabulary Builder</b> module helps students strengthen their vocabulary step by step.
945
+ It shows one main word, asks them to choose related words, and then helps them form sentences
946
+ using the correct words.
947
+ </li>
948
+ <li>
949
+ The activity is designed like a small game with clear cards, big buttons, and simple instructions,
950
+ so children can use it easily in class, in the lab, or at home.
951
+ </li>
952
+ <li>
953
+ It focuses on understanding word meanings, recognising related words, and using them in complete sentences.
954
+ </li>
955
+ </ul>
956
+ </li>
957
+
958
+ <li>
959
+ <b>Step 1 – Start the Vocabulary Builder</b><br>
960
+ <ul style="list-style: unset; margin-left: 1.25rem;">
961
+ <li>
962
+ On the first screen, students see the title <b>“Vocabulary Builder”</b> and a short introduction
963
+ explaining what they will do.
964
+ </li>
965
+ <li>
966
+ When they click the <b>“Let’s Build”</b> button, the system loads a new word and its options
967
+ in the background.
968
+ </li>
969
+ <li>
970
+ A loader overlay appears while the word and options are being fetched, so students understand
971
+ that the system is working and do not click repeatedly.
972
+ </li>
973
+ </ul>
974
+ </li>
975
+
976
+ <li>
977
+ <b>Step 2 – Choosing Related Words</b><br>
978
+ <ul style="list-style: unset; margin-left: 1.25rem;">
979
+ <li>
980
+ The next screen shows the main vocabulary word in a highlighted box and may include a small
981
+ image related to that word.
982
+ </li>
983
+ <li>
984
+ Below the word, several option buttons are shown. Students are asked to
985
+ <b>“Choose the related words (select only three)”</b>.
986
+ </li>
987
+ <li>
988
+ Students can select up to <b>three options</b>. Once three options are selected,
989
+ the other options are disabled so they cannot choose more than allowed.
990
+ </li>
991
+ <li>
992
+ A large action button (for example, <b>Check</b> or <b>Validate</b>) lets students confirm
993
+ their chosen words and move to the feedback stage.
994
+ </li>
995
+ </ul>
996
+ </li>
997
+
998
+ <li>
999
+ <b>Step 3 – Feedback on Word Selection</b><br>
1000
+ <ul style="list-style: unset; margin-left: 1.25rem;">
1001
+ <li>
1002
+ After validation, each option clearly shows whether it was a <b>correct</b> or
1003
+ <b>incorrect</b> choice using colours and simple styles (for example, green for correct
1004
+ and red for incorrect).
1005
+ </li>
1006
+ <li>
1007
+ A separate <b>Feedback</b> screen summarises the result with short points, such as which
1008
+ words were not related or why some choices were wrong.
1009
+ </li>
1010
+ <li>
1011
+ This helps students understand word relationships and avoid repeating the same mistake
1012
+ with similar words in future.
1013
+ </li>
1014
+ <li>
1015
+ A button at the bottom (for example, <b>Form a Sentence</b>) takes them to the next step
1016
+ where they will use the correct words in sentences.
1017
+ </li>
1018
+ </ul>
1019
+ </li>
1020
+
1021
+ <li>
1022
+ <b>Step 4 – Forming Sentences with Correct Words</b><br>
1023
+ <ul style="list-style: unset; margin-left: 1.25rem;">
1024
+ <li>
1025
+ In the sentence formation screen, the correctly related words are displayed in a list
1026
+ at the top, usually highlighted in a positive colour.
1027
+ </li>
1028
+ <li>
1029
+ For each correct word, there is a separate small textarea where the student has to
1030
+ <b>write a sentence using that word</b>.
1031
+ </li>
1032
+ <li>
1033
+ This moves the student from simply recognising the word to actually using it in
1034
+ meaningful context.
1035
+ </li>
1036
+ <li>
1037
+ The <b>Check Sentence</b> button remains disabled until all sentence boxes are filled.
1038
+ This ensures students write something for each word before asking for feedback.
1039
+ </li>
1040
+ </ul>
1041
+ </li>
1042
+
1043
+ <li>
1044
+ <b>Step 5 – Sentence Feedback and Reset</b><br>
1045
+ <ul style="list-style: unset; margin-left: 1.25rem;">
1046
+ <li>
1047
+ After checking, a <b>Sentence Feedback</b> screen shows clear comments on each sentence,
1048
+ such as whether it is correct or how it can be improved.
1049
+ </li>
1050
+ <li>
1051
+ Positive feedback (for example, correct answers) is highlighted clearly, and suggestions
1052
+ are given in simple language so children can understand what to change.
1053
+ </li>
1054
+ <li>
1055
+ A <b>Reset</b> or <b>Try Again</b> button allows students to start a new round with a
1056
+ different word, making the activity suitable for daily or weekly practice.
1057
+ </li>
1058
+ </ul>
1059
+ </li>
1060
+
1061
+ <li>
1062
+ <b>Navigation, Buttons and Safety</b><br>
1063
+ <ul style="list-style: unset; margin-left: 1.25rem;">
1064
+ <li>
1065
+ Each screen includes a <b>Back</b> icon and a <b>Close</b> button so students and teachers
1066
+ can move safely between steps or leave the exercise if needed.
1067
+ </li>
1068
+ <li>
1069
+ Buttons are disabled while the loader is visible, which prevents accidental double-clicks
1070
+ and keeps the flow stable.
1071
+ </li>
1072
+ <li>
1073
+ All main actions use the shared button style from the rest of the platform so the module
1074
+ looks consistent with other exercises.
1075
+ </li>
1076
+ </ul>
1077
+ </li>
1078
+
1079
+ <li>
1080
+ <b>Benefits for Students</b><br>
1081
+ <ul style="list-style: unset; margin-left: 1.25rem;">
1082
+ <li>
1083
+ Helps students <b>understand word meanings</b> by connecting a word with related words,
1084
+ not just memorising definitions.
1085
+ </li>
1086
+ <li>
1087
+ Trains them to <b>distinguish similar and unrelated words</b>, which is important for
1088
+ reading comprehension.
1089
+ </li>
1090
+ <li>
1091
+ Encourages students to <b>use new vocabulary in sentences</b>, improving both
1092
+ writing and speaking skills.
1093
+ </li>
1094
+ <li>
1095
+ Provides <b>instant, specific feedback</b> on both choices and sentences, so students
1096
+ know exactly what to correct.
1097
+ </li>
1098
+ <li>
1099
+ Can be reused many times with different words, making it a strong tool for regular
1100
+ vocabulary practice in class and at home.
1101
+ </li>
1102
+ </ul>
1103
+ </li>
1104
+ </ul>
1105
+ </ng-container>
1106
+
1107
+
1108
+ <!--Find Word-->
1109
+ <!-- Find the Word -->
1110
+ <ng-container *ngSwitchCase="'Find Word'">
1111
+ <ul style="text-align: justify; font-size: 15px;">
1112
+ <li>
1113
+ <b>Introduction</b><br>
1114
+ <ul style="list-style: unset; margin-left: 1.25rem;">
1115
+ <li>
1116
+ The <b>Find the Word</b> module is a listening and spelling exercise.
1117
+ Students listen to a spoken word, type what they hear, and receive
1118
+ immediate feedback.
1119
+ </li>
1120
+ <li>
1121
+ It is designed to connect <b>listening skills</b> with <b>correct spelling</b>,
1122
+ and also helps students learn the word’s meaning and example sentence.
1123
+ </li>
1124
+ <li>
1125
+ The activity uses a simple two-panel layout: an audio player on the left
1126
+ and a typing area with attempts and feedback on the right.
1127
+ </li>
1128
+ </ul>
1129
+ </li>
1130
+
1131
+ <li>
1132
+ <b>Step 1 – Start the Activity</b><br>
1133
+ <ul style="list-style: unset; margin-left: 1.25rem;">
1134
+ <li>
1135
+ The first screen explains that students will listen to a word and type
1136
+ it correctly to improve their word skills.
1137
+ </li>
1138
+ <li>
1139
+ When they click the <b>Start Learning</b> button, the module opens the
1140
+ main game screen.
1141
+ </li>
1142
+ <li>
1143
+ From the game screen, a <b>Back</b> icon and a <b>Close</b> button are
1144
+ always available at the top, so students and teachers can safely move
1145
+ back or exit the exercise at any time.
1146
+ </li>
1147
+ </ul>
1148
+ </li>
1149
+
1150
+ <li>
1151
+ <b>Step 2 – Listening to the Word (Audio Panel)</b><br>
1152
+ <ul style="list-style: unset; margin-left: 1.25rem;">
1153
+ <li>
1154
+ On the left side, students see the <b>audio panel</b> with:
1155
+ <ul style="list-style: disc; margin-left: 1.4rem;">
1156
+ <li>A <b>Generate audio</b> button to load a new word.</li>
1157
+ <li>A large <b>Play / Pause</b> button to listen to the word.</li>
1158
+ <li>A <b>progress bar</b> showing how much of the word has played.</li>
1159
+ </ul>
1160
+ </li>
1161
+ <li>
1162
+ When students click <b>Generate audio</b>, the system fetches a new
1163
+ audio word. While loading, the button and controls are temporarily
1164
+ disabled to prevent repeated clicks.
1165
+ </li>
1166
+ <li>
1167
+ The play button changes state while the audio is playing, and small
1168
+ animations (like moving bars) make it clear that sound is active.
1169
+ </li>
1170
+ <li>
1171
+ The audio can be replayed as many times as needed, so students can
1172
+ listen carefully before typing.
1173
+ </li>
1174
+ </ul>
1175
+ </li>
1176
+
1177
+ <li>
1178
+ <b>Step 3 – Typing the Word (Input Panel)</b><br>
1179
+ <ul style="list-style: unset; margin-left: 1.25rem;">
1180
+ <li>
1181
+ On the right side, students see the <b>Type the word</b> panel with:
1182
+ <ul style="list-style: disc; margin-left: 1.4rem;">
1183
+ <li>The title “Type the word”.</li>
1184
+ <li>Three <b>hearts</b> showing how many attempts are left.</li>
1185
+ <li>An input box for typing the word they heard.</li>
1186
+ <li>A <b>Submit</b> button to check their answer.</li>
1187
+ </ul>
1188
+ </li>
1189
+ <li>
1190
+ The input box is enabled only after the audio has played at least once.
1191
+ This encourages students to listen before they start typing.
1192
+ </li>
1193
+ <li>
1194
+ Each time the student submits an answer, the number of hearts (attempts)
1195
+ is updated. When there are no hearts left, the Submit button is disabled.
1196
+ </li>
1197
+ <li>
1198
+ If the answer is <b>correct</b>, the input box is highlighted in green
1199
+ and a small tick icon appears. If the answer is <b>wrong</b>, the box
1200
+ is highlighted in red and a clear message encourages the student to try again.
1201
+ </li>
1202
+ <li>
1203
+ A short <b>validation message</b> under the input box gives feedback in simple words
1204
+ (for example, “Try again” or “Well done!”).
1205
+ </li>
1206
+ </ul>
1207
+ </li>
1208
+
1209
+ <li>
1210
+ <b>Step 4 – Meaning, Example and Reset</b><br>
1211
+ <ul style="list-style: unset; margin-left: 1.25rem;">
1212
+ <li>
1213
+ Below the Submit button, there is an action bar with three options:
1214
+ <ul style="list-style: disc; margin-left: 1.4rem;">
1215
+ <li><b>📘 Meaning</b> – shows the meaning of the word.</li>
1216
+ <li><b>✍️ Example</b> – shows an example sentence using the word.</li>
1217
+ <li><b>⟲ Reset</b> – loads the next question or restarts the activity.</li>
1218
+ </ul>
1219
+ </li>
1220
+ <li>
1221
+ The Meaning and Example buttons are enabled only when it is appropriate
1222
+ (for example, after a word is loaded or after an attempt), so students
1223
+ follow the correct order.
1224
+ </li>
1225
+ <li>
1226
+ The Reset button becomes available when the student has finished their
1227
+ attempts or answered correctly. It clears the input and fetches a new word,
1228
+ making it easy to practise many words in one session.
1229
+ </li>
1230
+ <li>
1231
+ A popup message may also appear in the centre of the screen to give
1232
+ encouragement or clear information, with a Close button to return to the game.
1233
+ </li>
1234
+ </ul>
1235
+ </li>
1236
+
1237
+ <li>
1238
+ <b>Attempts, Safety and Controls</b><br>
1239
+ <ul style="list-style: unset; margin-left: 1.25rem;">
1240
+ <li>
1241
+ The three-heart system shows students exactly how many tries they have left.
1242
+ This keeps them focused and makes the exercise feel like a small challenge.
1243
+ </li>
1244
+ <li>
1245
+ Buttons are disabled during loading and when attempts are over, so there are
1246
+ no accidental double-clicks or confusing states.
1247
+ </li>
1248
+ <li>
1249
+ The Back and Close buttons at the top of the game screen allow teachers and
1250
+ students to leave the exercise at any time without losing control.
1251
+ </li>
1252
+ </ul>
1253
+ </li>
1254
+
1255
+ <li>
1256
+ <b>Benefits for Students</b><br>
1257
+ <ul style="list-style: unset; margin-left: 1.25rem;">
1258
+ <li>
1259
+ Improves <b>listening skills</b> by training students to hear individual sounds
1260
+ in English words.
1261
+ </li>
1262
+ <li>
1263
+ Strengthens <b>spelling and phonics</b>, as students must convert the sounds
1264
+ they hear into the correct letters.
1265
+ </li>
1266
+ <li>
1267
+ Builds <b>vocabulary</b> by linking each word with its meaning and an example sentence.
1268
+ </li>
1269
+ <li>
1270
+ Encourages <b>careful thinking</b> through limited attempts, instead of guessing
1271
+ many times without focus.
1272
+ </li>
1273
+ <li>
1274
+ Can be used as a short daily warm-up or regular practice activity in class,
1275
+ in the lab, or at home.
1276
+ </li>
1277
+ </ul>
1278
+ </li>
1279
+ </ul>
1280
+ </ng-container>
1281
+ </ng-container>
1282
+ </div>
1283
+ </div>
1284
+ </div>
1285
+
1286
+ <!-- ================= PRIVACY POLICY POPUP ================= -->
1287
+ <div *ngIf="showPrivacyPopup">
1288
+ <div class="user-guide-overlay" (click)="closePrivacyPopup()"></div>
1289
+ <div class="user-guide-modal" role="dialog" aria-modal="true" aria-labelledby="privacyTitle">
1290
+ <button class="user-guide-close-icon" (click)="closePrivacyPopup()" aria-label="Close">×</button>
1291
+ <ul style="text-align: justify; font-size: 15px;">
1292
+ <!-- Privacy Policy content (all used) -->
1293
+ <li id="privacyTitle">
1294
+ <b>Privacy Policy — Py-learn</b><br>
1295
+ <ul style="list-style: unset; margin-left: 1.25rem;">
1296
+ <li><b>Last Updated:</b> November 2025</li>
1297
+ <li><b>Who we are:</b> Py-learn is operated by <b>Pykara Technologies Private Limited</b> (“we”, “us”, “our”), the data controller for personal data processed through the platform.</li>
1298
+ <li><b>Contact:</b> <a href="mailto:info@pykara.ai">info&#64;pykara.ai</a></li>
1299
+ </ul>
1300
+ </li>
1301
+
1302
+ <li>
1303
+ <b>Overview</b><br>
1304
+ <ul style="list-style: unset; margin-left: 1.25rem;">
1305
+ <li>We collect limited personal data to deliver an AI-powered e-learning experience (chatbots, video lessons, quizzes) and to improve learning outcomes.</li>
1306
+ <li>We comply with applicable laws, including GDPR where relevant to users in the EEA/UK.</li>
1307
+ </ul>
1308
+ </li>
1309
+
1310
+ <li>
1311
+ <b>Information We Collect</b><br>
1312
+ <ul style="list-style: unset; margin-left: 1.25rem;">
1313
+ <li><b>Account Data:</b> name, email, password (hashed), profile details.</li>
1314
+ <li><b>Learning Data:</b> enrolled courses, lessons viewed, quiz results, progress analytics, feedback.</li>
1315
+ <li><b>Technical Data:</b> device/browser info, IP address, timestamps, logs, approximate location from IP.</li>
1316
+ <li><b>Cookies & Similar Tech:</b> preferences, session management, analytics (see “Cookies”).</li>
1317
+ </ul>
1318
+ </li>
1319
+
1320
+ <li>
1321
+ <b>Purposes of Processing</b><br>
1322
+ <ul style="list-style: unset; margin-left: 1.25rem;">
1323
+ <li>Provide and secure the service; authenticate users; maintain accounts.</li>
1324
+ <li>Personalise content, recommendations, and difficulty levels.</li>
1325
+ <li>Measure performance, improve features, and fix issues.</li>
1326
+ <li>Send essential service communications (policy changes, security notices).</li>
1327
+ <li>Comply with legal obligations and enforce Terms.</li>
1328
+ </ul>
1329
+ </li>
1330
+
1331
+ <li>
1332
+ <b>Legal Bases (GDPR)</b><br>
1333
+ <ul style="list-style: unset; margin-left: 1.25rem;">
1334
+ <li><b>Contract necessity:</b> to deliver core features you request.</li>
1335
+ <li><b>Legitimate interests:</b> service improvement, security, fraud prevention (balanced against your rights).</li>
1336
+ <li><b>Consent:</b> non-essential cookies, optional marketing (you can withdraw at any time).</li>
1337
+ <li><b>Legal obligation:</b> records, compliance, and requests from authorities where required by law.</li>
1338
+ </ul>
1339
+ </li>
1340
+
1341
+ <li>
1342
+ <b>Automated Processing & AI</b><br>
1343
+ <ul style="list-style: unset; margin-left: 1.25rem;">
1344
+ <li>AI is used to generate explanations, hints, and recommendations. We do not make decisions with legal or similarly significant effects solely by automated means.</li>
1345
+ </ul>
1346
+ </li>
1347
+
1348
+ <li>
1349
+ <b>Third-Party Processors & Transfers</b><br>
1350
+ <ul style="list-style: unset; margin-left: 1.25rem;">
1351
+ <li>We use reputable vendors for AI inference, hosting, analytics, email delivery, and content streaming. They act under contracts and process data only on our instructions.</li>
1352
+ <li>Where data is transferred outside your country (including outside the EEA/UK), appropriate safeguards are applied (e.g., Standard Contractual Clauses).</li>
1353
+ </ul>
1354
+ </li>
1355
+
1356
+ <li>
1357
+ <b>Data Sharing</b><br>
1358
+ <ul style="list-style: unset; margin-left: 1.25rem;">
1359
+ <li>With service providers under confidentiality and security obligations.</li>
1360
+ <li>For legal reasons (court orders, to protect users and our rights, prevent fraud or abuse).</li>
1361
+ <li>Business changes (merger, acquisition); we will ensure comparable protections or notify you of material changes.</li>
1362
+ </ul>
1363
+ </li>
1364
+
1365
+ <li>
1366
+ <b>Data Security & Retention</b><br>
1367
+ <ul style="list-style: unset; margin-left: 1.25rem;">
1368
+ <li>We apply technical and organisational measures (encryption at rest/in transit, access controls, monitoring).</li>
1369
+ <li>We retain personal data only as long as needed for the purposes above or as required by law. <b>Inactive accounts may be deleted after 24 months.</b></li>
1370
+ </ul>
1371
+ </li>
1372
+
1373
+ <li>
1374
+ <b>Cookies & Similar Technologies</b><br>
1375
+ <ul style="list-style: unset; margin-left: 1.25rem;">
1376
+ <li><b>Essential:</b> login/session, security, load balancing.</li>
1377
+ <li><b>Preferences:</b> UI settings, language, playback choices.</li>
1378
+ <li><b>Analytics:</b> usage statistics to improve features.</li>
1379
+ <li>You may control cookies via your browser. Disabling some cookies may affect functionality.</li>
1380
+ </ul>
1381
+ </li>
1382
+
1383
+ <li>
1384
+ <b>Children’s Privacy</b><br>
1385
+ <ul style="list-style: unset; margin-left: 1.25rem;">
1386
+ <li>The service is intended for users aged 13+. If we learn that data from a child under 13 was collected without appropriate consent, we will delete it.</li>
1387
+ </ul>
1388
+ </li>
1389
+
1390
+ <li>
1391
+ <b>Your Rights (EEA/UK where applicable)</b><br>
1392
+ <ul style="list-style: unset; margin-left: 1.25rem;">
1393
+ <li>Access, rectify, erase, restrict processing, object, and data portability.</li>
1394
+ <li>Withdraw consent (for consent-based processing) without affecting prior lawful processing.</li>
1395
+ <li>Lodge a complaint with a supervisory authority. We encourage contacting us first at <a href="mailto:info@pykara.ai">info&#64;pykara.ai</a>.</li>
1396
+ </ul>
1397
+ </li>
1398
+
1399
+ <li>
1400
+ <b>Data Breach Notification</b><br>
1401
+ <ul style="list-style: unset; margin-left: 1.25rem;">
1402
+ <li>We will notify affected users and relevant authorities when legally required.</li>
1403
+ </ul>
1404
+ </li>
1405
+
1406
+ <li>
1407
+ <b>Changes to this Policy</b><br>
1408
+ <ul style="list-style: unset; margin-left: 1.25rem;">
1409
+ <li>We may update this policy. The “Last Updated” date reflects the latest version; material changes may be additionally notified in-app or by email.</li>
1410
+ </ul>
1411
+ </li>
1412
+
1413
+ <li>
1414
+ <b>Contact</b><br>
1415
+ <ul style="list-style: unset; margin-left: 1.25rem;">
1416
+ <li>Email: <a href="mailto:info@pykara.ai">info&#64;pykara.ai</a></li>
1417
+ <li>Registered Office: Pykara Technologies Private Limited, Chennai, Tamil Nadu, India</li>
1418
+ </ul>
1419
+ </li>
1420
+ </ul>
1421
+ </div>
1422
+ </div>
1423
+
1424
+ <!-- ================= TERMS & CONDITIONS POPUP ================= -->
1425
+ <div *ngIf="showTermsPopup">
1426
+ <div class="user-guide-overlay" (click)="closeTermsPopup()"></div>
1427
+ <div class="user-guide-modal" role="dialog" aria-modal="true" aria-labelledby="termsTitle">
1428
+ <button class="user-guide-close-icon" (click)="closeTermsPopup()" aria-label="Close">×</button>
1429
+ <ul style="text-align: justify; font-size: 15px;">
1430
+ <!-- Terms & Conditions content (all used) -->
1431
+ <li id="termsTitle">
1432
+ <b>Terms & Conditions — Py-learn</b><br>
1433
+ <ul style="list-style: unset; margin-left: 1.25rem;">
1434
+ <li><b>Last Updated:</b> November 2025</li>
1435
+ <li>By using Py-learn, you agree to these Terms and our Privacy Policy.</li>
1436
+ </ul>
1437
+ </li>
1438
+
1439
+ <li>
1440
+ <b>Eligibility & Accounts</b><br>
1441
+ <ul style="list-style: unset; margin-left: 1.25rem;">
1442
+ <li>You must be 13+ (or the age of digital consent in your region). Users under 18 require parental/guardian consent.</li>
1443
+ <li>You are responsible for the accuracy of your information and for safeguarding your credentials.</li>
1444
+ </ul>
1445
+ </li>
1446
+
1447
+ <li>
1448
+ <b>Permitted Use & Prohibited Conduct</b><br>
1449
+ <ul style="list-style: unset; margin-left: 1.25rem;">
1450
+ <li>Use the service for lawful, educational purposes only.</li>
1451
+ <li>Do not attempt unauthorised access, disrupt the service, reverse engineer components, or misuse AI features.</li>
1452
+ <li>International users must comply with local laws.</li>
1453
+ </ul>
1454
+ </li>
1455
+
1456
+ <li>
1457
+ <b>Content & Intellectual Property</b><br>
1458
+ <ul style="list-style: unset; margin-left: 1.25rem;">
1459
+ <li>All software, UI, designs, text, graphics, videos, and datasets are owned by Pykara Technologies or its licensors.</li>
1460
+ <li>Copying, redistribution, or commercial use requires prior written consent.</li>
1461
+ <li>When you submit content (e.g., answers, uploads), you grant us a non-exclusive, royalty-free licence to host and process it to operate and improve the service (including for AI model quality and safety, where allowed by law and your settings).</li>
1462
+ </ul>
1463
+ </li>
1464
+
1465
+ <li>
1466
+ <b>AI Outputs & Educational Disclaimer</b><br>
1467
+ <ul style="list-style: unset; margin-left: 1.25rem;">
1468
+ <li>AI responses support learning but may contain errors. Verify outputs before relying on them; they are not professional advice.</li>
1469
+ </ul>
1470
+ </li>
1471
+
1472
+ <li>
1473
+ <b>Fees, Trials & Refunds</b><br>
1474
+ <ul style="list-style: unset; margin-left: 1.25rem;">
1475
+ <li>Some features may be paid (subscriptions or one-off fees). Prices and taxes are shown at checkout.</li>
1476
+ <li>Payments are processed securely by third-party providers. Unless required by law, payments are non-refundable.</li>
1477
+ </ul>
1478
+ </li>
1479
+
1480
+ <li>
1481
+ <b>Third-Party Links & Services</b><br>
1482
+ <ul style="list-style: unset; margin-left: 1.25rem;">
1483
+ <li>We may link to third-party content or integrate with providers (hosting, analytics, AI). We are not responsible for third-party terms or policies.</li>
1484
+ </ul>
1485
+ </li>
1486
+
1487
+ <li>
1488
+ <b>Termination & Suspension</b><br>
1489
+ <ul style="list-style: unset; margin-left: 1.25rem;">
1490
+ <li>We may suspend or terminate access for violations, fraud, or security risks. You may request account closure at any time.</li>
1491
+ </ul>
1492
+ </li>
1493
+
1494
+ <li>
1495
+ <b>Warranties & Liability</b><br>
1496
+ <ul style="list-style: unset; margin-left: 1.25rem;">
1497
+ <li>The service is provided “as is” and “as available” without warranties of any kind.</li>
1498
+ <li>To the fullest extent permitted by law, we are not liable for indirect, incidental, special, consequential, or punitive damages.</li>
1499
+ </ul>
1500
+ </li>
1501
+
1502
+ <li>
1503
+ <b>Indemnity</b><br>
1504
+ <ul style="list-style: unset; margin-left: 1.25rem;">
1505
+ <li>You agree to indemnify and hold us harmless from claims arising from your misuse of the service or breach of these Terms.</li>
1506
+ </ul>
1507
+ </li>
1508
+
1509
+ <li>
1510
+ <b>Changes to the Service or Terms</b><br>
1511
+ <ul style="list-style: unset; margin-left: 1.25rem;">
1512
+ <li>We may modify features or these Terms. Continued use after changes indicates acceptance; material updates may be additionally notified in-app or by email.</li>
1513
+ </ul>
1514
+ </li>
1515
+
1516
+ <li>
1517
+ <b>Governing Law & Disputes</b><br>
1518
+ <ul style="list-style: unset; margin-left: 1.25rem;">
1519
+ <li>Governing law: India. Exclusive jurisdiction: courts in Chennai, Tamil Nadu, India.</li>
1520
+ </ul>
1521
+ </li>
1522
+
1523
+ <li>
1524
+ <b>Contact</b><br>
1525
+ <ul style="list-style: unset; margin-left: 1.25rem;">
1526
+ <li>Support & legal queries: <a href="mailto:info@pykara.ai">info&#64;pykara.ai</a></li>
1527
+ <li>Registered Office: Pykara Technologies Private Limited, Chennai, Tamil Nadu, India</li>
1528
+ </ul>
1529
+ </li>
1530
+ </ul>
1531
  </div>
1532
  </div>
src/app/home/home.component.ts CHANGED
@@ -1,261 +1,161 @@
1
- import {
2
- Component,
3
- ElementRef,
4
- QueryList,
5
- ViewChildren,
6
- AfterViewInit
7
- } from '@angular/core';
8
  import { Router } from '@angular/router';
 
 
 
9
 
10
  @Component({
11
  selector: 'app-home',
12
  templateUrl: './home.component.html',
13
  styleUrls: ['./home.component.css']
14
  })
15
- export class HomeComponent implements AfterViewInit {
16
- @ViewChildren('wrapper') wrappers!: QueryList<ElementRef>;
17
- // component.ts
18
- isVoiceDisabled = true; // set true to disable, false to enable
19
-
 
 
 
 
 
 
 
 
 
 
 
20
  cards = [
21
- {
22
- title: 'Grammar Chat',
23
- description: `
24
- This Chat module is a fun and interactive chatbox where students can ask only the grammar-related questions and receive structured, dynamic responses from the bot.
25
- It supports both text input and speech recognition, allowing users to either type or speak their queries.
26
-
27
- Students can also select from a list of predefined grammar questions shown when the input box is focused.
28
- The chat area simulates a live conversation.
29
-
30
- This module also supports voice output. Users can listen to the responses using a built-in voice or their teacher's voice, based on their choice.
31
- Audio controls are provided for better interactivity.
32
-
33
- It helps students educate themselves and solve their grammar-related questions, as if they are getting trained by their own teachers in the classroom.
34
- `,
35
- image: 'assets/images/home/Grammar_chat.png',
36
- action: () => this.goToChat()
37
- },
38
- {
39
- title: 'Grammar Quiz',
40
- description: `
41
- This module helps students practice grammar through an engaging fill-in-the-blank activity, which is dynamically generated based on the topic they choose.
42
-
43
- The exercise follows a level-based progression system. It begins with simple questions and gradually increases in difficulty as the student advances.
44
-
45
- Each question includes blanks that students must complete with the correct words. The answers are validated in real-time, encouraging immediate learning and correction.
46
-
47
- Students are allowed two attempts for each question. If a student enters an incorrect answer in the first attempt, they receive another chance to try again.
48
- If both attempts are incorrect or left incomplete, the correct answer is shown, and the system moves to the next level automatically.
49
-
50
- Helpful hints are displayed after an incorrect attempt to support learning. However, validation is only enabled when all blanks are filled, ensuring complete engagement in the exercise.
51
-
52
- Once all levels are completed, a congratulatory message is displayed to encourage and motivate the learner.
53
-
54
- This module helps students test their grammar knowledge in a structured and interactive manner, similar to how they would be assessed in a classroom environment.
55
- `,
56
- image: 'assets/images/home/quiz.png',
57
- action: () => this.goToGenerateQuestions()
58
- },
59
- {
60
- title: 'Voice',
61
- description: `
62
- The Voice module helps students improve their pronunciation through guided listening and speaking exercises.
63
-
64
-
65
- The system plays the correct pronunciation of a word using a default voice. Students listen carefully and then repeat the word using their microphone.
66
- The spoken word is recorded and compared with the correct version, and students receive clear feedback on their pronunciation accuracy.
67
-
68
- A special feature of this module is the option to use a custom voice. Schools or teachers can upload their own recorded voice to replace the system’s default pronunciation. T
69
- his allows students to learn using a familiar or preferred voice, creating a more personalized and effective learning experience.
70
-
71
- This module supports repeated practice and encourages students to speak clearly and confidently. By combining listening, speaking, and guided feedback,
72
- it helps students build strong pronunciation skills.
73
-
74
- It also allows students to test and improve their spoken grammar in a classroom-like environment.
75
- `,
76
- image: 'assets/images/home/voice.png',
77
- action: () => this.goToVoice()
78
- },
79
- {
80
- title: 'Listening',
81
- description: `
82
- This module helps students improve their listening comprehension through video-based exercises designed for interactive learning.
83
-
84
- Students can watch a video selected by their teacher or use a default video provided in the system. Once the video is completed, the Generate Questions button becomes active.
85
- Students can then attempt a set of multiple-choice questions based on the video content.
86
-
87
- Each question includes several options, and students are required to select the most appropriate answer. After responding to all the questions, they can submit their answers.
88
- The system will immediately show which responses are correct and which are incorrect, offering valuable feedback.
89
-
90
- Once a question is answered, it becomes locked to prevent changes, and all answer attempts are tracked to ensure fair evaluation.
91
-
92
- A key feature of this module is the flexibility to upload custom videos, allowing teachers to personalize the listening material as per the learning goals.
93
-
94
- This module helps students develop active listening skills and improves their understanding of spoken English in real-world contexts.
95
- It also gives students an opportunity to test their listening skills based on the uploaded video and improve their understanding in a classroom-like setting.
96
- `,
97
- image: 'assets/images/home/Listening.png',
98
- action: () => this.goToListen()
99
- },
100
- {
101
- title: 'Reading',
102
- description: `
103
- This reading module is designed to help students improve their comprehension and thinking skills.
104
-
105
- Students begin by entering a topic and selecting a difficulty level—easy, medium, or hard. Based on their input, a reading passage is generated, followed by related multiple-choice questions.
106
-
107
- When the input field is focused, a list of predefined topic suggestions is shown to guide students in choosing a suitable topic.
108
-
109
- Students read the passage carefully and answer each question. After submitting their answers, the system shows which responses are correct and which are incorrect, along with helpful feedback.
110
-
111
- This exercise encourages students to read with attention and apply their understanding in a clear and structured manner.
112
-
113
- It also helps students practice reading and answering questions in a way that feels similar to a regular classroom environment.
114
- `,
115
- image: 'assets/images/home/Reading.png',
116
- action: () => this.goToRead()
117
- },
118
- {
119
- title: 'Writing',
120
- description: `
121
- This writing module generates custom writing topics based on the selected grade level.
122
-
123
- Students can choose their grade level—Lower, Middle, or Upper—and the system will provide an appropriate writing topic.
124
- Once the topic is displayed, students write their responses in a structured text area and submit them for feedback.
125
-
126
- The system provides feedback on grammar, clarity, and sentence structure to help students improve their writing skills.
127
- Each session allows a single submission, with a reset option for students to try again if they wish.
128
-
129
- This exercise helps students develop their writing abilities by guiding them to express their thoughts clearly and correctly.
130
- `,
131
- image: 'assets/images/home/writting.png',
132
- action: () => this.goToWrite()
133
- },
134
- {
135
- title: 'Vocabulary Builder',
136
- description: `
137
- This module helps students enhance their vocabulary through word selection and sentence creation exercises.
138
-
139
- Students begin by selecting three related words from a multiple-choice list, which is dynamically generated by AI. After selecting the words, students are prompted to form sentences using these chosen words.
140
-
141
- Using the three correct words, students need to form sentences in the respective areas. Each sentence is then evaluated, and students receive feedback on their usage.
142
-
143
- This activity promotes vocabulary expansion and helps students learn to use words meaningfully in context.
144
-
145
- It provides a structured approach to developing vocabulary and improving sentence formation skills.
146
- `,
147
- image: 'assets/images/home/vocabulary.png',
148
- action: () => this.goToVocubalary()
149
- },
150
- {
151
- title: 'Find Word',
152
- description: `
153
- This module enhances both listening and vocabulary skills through audio-based word recognition.
154
-
155
- Students listen to a word, type what they heard, and receive instant feedback on their answer. and if the student enter the wrong answer there have 2 attempt after the 2 attempt it
156
- automatically show the correct answer.
157
-
158
- After validating the answer, students can view the word's meaning if student enabe to answer there can view the meaning so that student will able to answer otherwise there have 2 attempt
159
- and an example sentence, helping them understand the word in context. Once they complete this step, they can move on to the next round.
160
-
161
- This activity supports the development of spelling accuracy, active listening, and contextual word usage.
162
-
163
-
164
-
165
- This module enhances both listening and vocabulary skills through audio-based word recognition.
166
-
167
- Students listen to a word, type what they heard, and receive instant feedback on their answer. If the student enters the wrong answer, they have two attempts to correct it. After the second attempt, the correct answer is automatically shown.
168
-
169
- Additionally, an example sentence is provided to help students understand how to use the word in context. Once this step is completed, students can move on to the next round.
170
-
171
- This activity helps improve spelling accuracy, active listening, and contextual word usage.
172
- `,
173
- image: 'assets/images/home/find_word.png',
174
- action: () => this.goToFindword()
175
- }
176
  ];
177
 
178
- collapsedStates: boolean[] = [];
179
- isOverflowing: boolean[] = [];
180
- menuOpen = false;
181
-
182
- constructor(private router: Router) { }
 
 
 
 
 
 
 
 
 
 
 
 
 
183
 
184
- ngAfterViewInit(): void {
185
- this.collapsedStates = this.cards.map(() => true);
186
- this.isOverflowing = this.cards.map(() => false);
187
 
188
- // Delay to ensure DOM is rendered
189
- setTimeout(() => {
190
- this.wrappers.forEach((wrapper, index) => {
191
- const el = wrapper.nativeElement as HTMLElement;
192
- const maxHeight = parseFloat(getComputedStyle(el).maxHeight);
193
- if (el.scrollHeight > maxHeight) {
194
- this.isOverflowing[index] = true;
195
- }
196
- });
197
- }, 0);
198
  }
199
 
200
- toggleCard(index: number): void {
201
- this.collapsedStates[index] = !this.collapsedStates[index];
202
-
203
- if (this.collapsedStates[index]) {
204
- setTimeout(() => {
205
- const wrapper = this.wrappers.toArray()[index];
206
- if (wrapper) {
207
- wrapper.nativeElement.scrollTo({ top: 0, behavior: 'smooth' });
208
- }
209
- }, 0);
210
- }
211
  }
212
 
213
- toggleMenu(): void {
214
- this.menuOpen = !this.menuOpen;
 
 
 
215
  }
216
 
217
- reloadPage(): void {
218
- window.location.href = '/';
219
  }
220
 
221
- navigateTo(path: string): void {
222
- const routes = ['chat', 'practice', 'voice'];
223
- if (routes.includes(path)) {
224
- this.router.navigate([`/${path}`]);
225
- } else {
226
- console.error('Invalid path:', path);
 
 
 
227
  }
228
  }
229
 
230
- goToChat(): void {
231
- this.router.navigate(['/chat']);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
232
  }
233
 
234
- goToGenerateQuestions(): void {
235
- this.router.navigate(['/generate-questions']);
 
236
  }
237
 
238
- goToVoice(): void {
239
- this.router.navigate(['/voice']);
 
 
240
  }
241
 
242
- goToListen(): void {
243
- this.router.navigate(['/listen']);
 
 
 
 
 
 
 
 
 
 
 
244
  }
245
 
246
- goToWrite(): void {
247
- this.router.navigate(['/writing']);
 
 
248
  }
249
 
250
- goToRead(): void {
251
- this.router.navigate(['/reading']);
252
  }
253
 
254
- goToVocubalary(): void {
255
- this.router.navigate(['/vocabulary-builder']);
 
256
  }
257
 
258
- goToFindword(): void {
259
- this.router.navigate(['/findword']);
260
  }
261
  }
 
1
+ import { Component, AfterViewInit, OnInit, OnDestroy, ElementRef, HostListener } from '@angular/core';
 
 
 
 
 
 
2
  import { Router } from '@angular/router';
3
+ import { AuthService } from '../auth/auth.service';
4
+ import { Subscription } from 'rxjs';
5
+ import { BrandService } from '../shared/brand.service';
6
 
7
  @Component({
8
  selector: 'app-home',
9
  templateUrl: './home.component.html',
10
  styleUrls: ['./home.component.css']
11
  })
12
+ export class HomeComponent implements AfterViewInit, OnInit, OnDestroy {
13
+ // -------------------- UI State --------------------
14
+ menuOpen = false;
15
+ showGuidePopup = false;
16
+ selectedCardTitle: string | null = null;
17
+ showAccountMenu = false;
18
+ showPrivacyPopup = false;
19
+ showTermsPopup = false;
20
+ isVoiceDisabled = true;
21
+
22
+ // -------------------- Authentication State --------------------
23
+ isAuthenticated = false;
24
+ username: string | null = null;
25
+ private authSub?: Subscription;
26
+
27
+ // -------------------- Card Data --------------------
28
  cards = [
29
+ { title: 'Grammar Chat', image: 'assets/images/home/Grammar_chat.png', action: () => this.goToChat() },
30
+ { title: 'Grammar Quiz', image: 'assets/images/home/quiz.png', action: () => this.goToGenerateQuestions() },
31
+ { title: 'Voice', image: 'assets/images/home/voice.png', action: () => this.goToVoice() },
32
+ { title: 'Listening', image: 'assets/images/home/Listening.png', action: () => this.goToListen() },
33
+ { title: 'Reading', image: 'assets/images/home/Reading.png', action: () => this.goToRead() },
34
+ { title: 'Writing', image: 'assets/images/home/writting.png', action: () => this.goToWrite() },
35
+ { title: 'Vocabulary Builder', image: 'assets/images/home/vocabulary.png', action: () => this.goToVocubalary() },
36
+ { title: 'Find Word', image: 'assets/images/home/find_word.png', action: () => this.goToFindword() }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  ];
38
 
39
+ // -------------------- Constructor --------------------
40
+ constructor(
41
+ private router: Router,
42
+ private authService: AuthService,
43
+ private host: ElementRef,
44
+ public brand: BrandService
45
+ ) { }
46
+
47
+ // -------------------- Lifecycle Hooks --------------------
48
+ ngOnInit(): void {
49
+ this.isAuthenticated = this.authService.isLoggedIn();
50
+ this.username = localStorage.getItem('username');
51
+ this.authSub = this.authService.isLoggedIn$.subscribe((v) => {
52
+ this.isAuthenticated = v;
53
+ this.username = v ? localStorage.getItem('username') : null;
54
+ if (!v) this.showAccountMenu = false;
55
+ });
56
+ }
57
 
58
+ ngAfterViewInit(): void { }
 
 
59
 
60
+ ngOnDestroy(): void {
61
+ this.authSub?.unsubscribe();
 
 
 
 
 
 
 
 
62
  }
63
 
64
+ // -------------------- Avatar Helpers --------------------
65
+ get usernameInitial(): string {
66
+ const u = this.username || '';
67
+ return u.trim().charAt(0).toUpperCase() || 'U';
 
 
 
 
 
 
 
68
  }
69
 
70
+ get displayName(): string {
71
+ const u = this.username || '';
72
+ if (!u) return '';
73
+ const name = u.includes('@') ? u.split('@')[0] : u;
74
+ return name.replace(/\b\w/g, (c) => c.toUpperCase());
75
  }
76
 
77
+ get displayEmail(): string {
78
+ return this.username || '';
79
  }
80
 
81
+ // -------------------- Account Menu Controls --------------------
82
+ toggleAccountMenu(): void { this.showAccountMenu = !this.showAccountMenu; }
83
+ openAccountMenu(): void { this.showAccountMenu = true; }
84
+ closeAccountMenu(): void { this.showAccountMenu = false; }
85
+
86
+ @HostListener('document:click', ['$event'])
87
+ onDocClick(ev: MouseEvent) {
88
+ if (!this.host.nativeElement.contains(ev.target)) {
89
+ this.showAccountMenu = false;
90
  }
91
  }
92
 
93
+ // -------------------- Main Menu Controls --------------------
94
+ toggleMenu(): void { this.menuOpen = !this.menuOpen; }
95
+
96
+ // -------------------- Navigation --------------------
97
+ reloadPage(): void { window.location.href = '/'; }
98
+
99
+ goToChat(): void { this.router.navigate(['/chat']); }
100
+ goToGenerateQuestions(): void { this.router.navigate(['/generate-questions']); }
101
+ goToVoice(): void { this.router.navigate(['/voice']); }
102
+ goToListen(): void { this.router.navigate(['/listen']); }
103
+ goToWrite(): void { this.router.navigate(['/writing']); }
104
+ goToRead(): void { this.router.navigate(['/reading']); }
105
+ goToVocubalary(): void { this.router.navigate(['/vocabulary-builder']); }
106
+ goToFindword(): void { this.router.navigate(['/findword']); }
107
+ goToDetails(title: string): void {
108
+ this.router.navigate(['/details'], { queryParams: { topic: title } });
109
+ }
110
+
111
+ // --------------------User Guide Popup Controls --------------------
112
+ openGuidePopup(title: string): void {
113
+ this.selectedCardTitle = title;
114
+ this.showGuidePopup = true;
115
  }
116
 
117
+ closeGuidePopup(): void {
118
+ this.showGuidePopup = false;
119
+ this.selectedCardTitle = null;
120
  }
121
 
122
+ // -------------------- Account Actions --------------------
123
+ goToAccount(): void {
124
+ this.router.navigate(['/home']);
125
+ this.showAccountMenu = false;
126
  }
127
 
128
+ logout(): void {
129
+ this.authService.logout().subscribe({
130
+ next: () => {
131
+ localStorage.removeItem('username');
132
+ this.showAccountMenu = false;
133
+ this.router.navigate(['/home']);
134
+ },
135
+ error: () => {
136
+ localStorage.removeItem('username');
137
+ this.showAccountMenu = false;
138
+ this.router.navigate(['/home']);
139
+ }
140
+ });
141
  }
142
 
143
+ // -------------------- privacy and terms and condition Popup Controls --------------------
144
+ openPrivacyPopup(event: Event): void {
145
+ event.preventDefault();
146
+ this.showPrivacyPopup = true;
147
  }
148
 
149
+ closePrivacyPopup(): void {
150
+ this.showPrivacyPopup = false;
151
  }
152
 
153
+ openTermsPopup(event: Event): void {
154
+ event.preventDefault();
155
+ this.showTermsPopup = true;
156
  }
157
 
158
+ closeTermsPopup(): void {
159
+ this.showTermsPopup = false;
160
  }
161
  }
src/app/shared/brand.service.ts ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Injectable } from '@angular/core';
2
+ import { BehaviorSubject } from 'rxjs';
3
+
4
+ @Injectable({ providedIn: 'root' })
5
+ export class BrandService {
6
+ private nameSubject = new BehaviorSubject<string>('Py-Learn');
7
+ private logoSubject = new BehaviorSubject<string>('assets/images/pykara-logo.png');
8
+
9
+ readonly name$ = this.nameSubject.asObservable();
10
+ readonly logo$ = this.logoSubject.asObservable();
11
+
12
+ get name(): string { return this.nameSubject.value; }
13
+ get logo(): string { return this.logoSubject.value; }
14
+
15
+ setName(name: string) { this.nameSubject.next(name); }
16
+ setLogo(path: string) { this.logoSubject.next(path); }
17
+ }
src/app/shared/header/header.component.css ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Reuse global styles; keep only minor tweaks if needed */
2
+ :host {
3
+ display: block;
4
+ }
5
+
6
+ .header-container {
7
+ display: flex;
8
+ align-items: center;
9
+ gap: 1rem;
10
+ justify-content: space-between;
11
+ padding: 0 2vw;
12
+ background-color: #009688;
13
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
14
+ width: 100%;
15
+ position: sticky;
16
+ top: 0;
17
+ z-index: 1000;
18
+ overflow-x: clip;
19
+ }
20
+
21
+ .header-container .logo,
22
+ .header-container .home-btn,
23
+ .header-container .toggle-buttons-container {
24
+ flex: 0 0 auto;
25
+ }
26
+
27
+ .header-title {
28
+ flex: 1 1 auto;
29
+ min-width: 0;
30
+ text-align: center;
31
+ }
32
+
33
+ .header-title h1 {
34
+ font-size: 3vw;
35
+ color: #fff;
36
+ margin: 0;
37
+ line-height: 1.2;
38
+ white-space: nowrap;
39
+ overflow: hidden;
40
+ text-overflow: ellipsis;
41
+ position: absolute;
42
+ top: 50%;
43
+ left: 50%;
44
+ transform: translate(-50%, -50%);
45
+ }
46
+
47
+ .header-container .logo img {
48
+ max-width: 5vw;
49
+ height: auto;
50
+ background: #fff;
51
+ border-radius: 1vw;
52
+ margin: 0.5vw;
53
+ }
54
+
55
+ .home-btn img {
56
+ width: 5vw;
57
+ transition: transform 0.2s ease;
58
+ }
59
+
60
+ .home-btn img:hover {
61
+ transform: scale(1.08);
62
+ }
63
+
64
+
65
+ .logo {
66
+ display: flex;
67
+ align-items: center;
68
+ gap: 1vw;
69
+ }
70
+
71
+ .brand-link {
72
+ display: flex;
73
+ align-items: center;
74
+ gap: 0.6vw;
75
+ text-decoration: none;
76
+ }
77
+
78
+ .product-name {
79
+ color: #fff;
80
+ font-size: 2vw;
81
+ letter-spacing: 0.5px;
82
+ font-family: Amonk_Outline;
83
+ line-height: 1;
84
+ }
85
+
86
+ .brand-link:hover .product-name {
87
+ opacity: 0.9;
88
+ }
89
+
90
+ .header-title h1 {
91
+ font-family: 'Super Cartoon', cursive;
92
+ }
src/app/shared/header/header.component.html ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="header-container">
2
+ <div class="logo">
3
+ <a routerLink="/home" class="brand-link">
4
+ <img [src]="logoSrc || brand.logo" [alt]="(productName || brand.name) + ' Logo'" />
5
+ </a>
6
+ <span class="product-name">{{ productName || brand.name }}</span>
7
+ </div>
8
+
9
+ <div class="header-title" *ngIf="title">
10
+ <h1>{{ title }}</h1>
11
+ </div>
12
+
13
+ <div class="toggle-buttons-container modern-toggle">
14
+ <ng-content select="[header-right]"></ng-content>
15
+ </div>
16
+
17
+ <div class="home-btn" *ngIf="showHome">
18
+ <a routerLink="/home">
19
+ <img src="assets/images/home.png" alt="Home" class="home-icon" />
20
+ </a>
21
+ </div>
22
+ </div>
src/app/shared/header/header.component.ts ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Component, Input } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import { RouterModule } from '@angular/router';
4
+ import { BrandService } from '../brand.service';
5
+
6
+ @Component({
7
+ selector: 'app-header',
8
+ standalone: true,
9
+ imports: [CommonModule, RouterModule],
10
+ templateUrl: './header.component.html',
11
+ styleUrls: ['./header.component.css']
12
+ })
13
+ export class HeaderComponent {
14
+ @Input() title: string | null = null;
15
+ @Input() showHome: boolean = true;
16
+ @Input() logoSrc?: string; // optional override
17
+ @Input() productName?: string; // optional override
18
+
19
+ constructor(public brand: BrandService) {}
20
+ }
src/app/sign-in/sign-in.component.css ADDED
@@ -0,0 +1,1889 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* root colors and variables remain unchanged */
2
+
3
+ .signin-popup {
4
+ position: fixed;
5
+ top: 0;
6
+ left: 0;
7
+ width: 100vw;
8
+ height: 100vh;
9
+ display: flex;
10
+ flex-direction: column;
11
+ align-items: center;
12
+ justify-content: center;
13
+ z-index: 1000;
14
+ background: #05234b;
15
+ backdrop-filter: blur(16px);
16
+ }
17
+
18
+ /* Ensure no solid white background is set anywhere */
19
+
20
+ .signin-header {
21
+ width: 420px;
22
+ max-width: 95vw;
23
+ display: flex;
24
+ justify-content: flex-end;
25
+ align-items: flex-start;
26
+ padding: 0;
27
+ position: absolute;
28
+ top: -28px;
29
+ right: 12px;
30
+ pointer-events: none;
31
+ z-index: 2;
32
+ }
33
+ .signin-logo {
34
+ display: flex;
35
+ align-items: center;
36
+ gap: 12px;
37
+ pointer-events: auto;
38
+ }
39
+ .signin-login-link {
40
+ font-size: 1.1rem;
41
+ color: #fff;
42
+ margin-top: 0;
43
+ pointer-events: auto;
44
+ background: #18314a;
45
+ border-radius: 0 0 12px 12px;
46
+ padding: 8px 18px 4px 18px;
47
+ box-shadow: 0 2px 8px #0002;
48
+ position: relative;
49
+ right: 0;
50
+ top: 0;
51
+ }
52
+ .signin-login-link a {
53
+ color: #1de9b6;
54
+ text-decoration: underline;
55
+ margin-left: 6px;
56
+ font-weight: 600;
57
+ cursor: pointer;
58
+ }
59
+ .signin-box {
60
+ margin: 0 auto;
61
+ /* Center the box horizontally and vertically */
62
+ position: relative;
63
+ left: 0;
64
+ right: 0;
65
+ top: 0;
66
+ bottom: 0;
67
+ background: #18314a;
68
+ border-radius: 22px;
69
+ box-shadow: 0 12px 48px #000a;
70
+ padding: 48px 38px 32px 38px;
71
+ width: 420px;
72
+ max-width: 95vw;
73
+ display: flex;
74
+ flex-direction: column;
75
+ align-items: center;
76
+ }
77
+ .signin-title {
78
+ color: #38bdf8;
79
+ font-size: 2.1rem;
80
+ font-weight: 800;
81
+ margin-bottom: 12px;
82
+ text-align: center;
83
+ letter-spacing: 1px;
84
+ text-shadow: 0 2px 8px #0008;
85
+ }
86
+ form {
87
+ width: 100%;
88
+ }
89
+ .signin-row {
90
+ display: flex;
91
+ gap: 24px;
92
+ margin-bottom: 18px;
93
+ }
94
+ .signin-field {
95
+ flex: 1;
96
+ display: flex;
97
+ flex-direction: column;
98
+ }
99
+ .signin-field label {
100
+ color: #fff;
101
+ font-weight: 600;
102
+ margin-bottom: 6px;
103
+ font-size: 1rem;
104
+ letter-spacing: 0.5px;
105
+ }
106
+ .signin-field input {
107
+ background: #fff;
108
+ color: #18314a;
109
+ border: none;
110
+ border-radius: 8px;
111
+ padding: 12px 14px;
112
+ font-size: 1rem;
113
+ margin-bottom: 2px;
114
+ box-shadow: 0 1px 4px #0002;
115
+ transition: border 0.2s, box-shadow 0.2s;
116
+ }
117
+ .signin-field input:focus {
118
+ outline: 2px solid #1de9b6;
119
+ border-color: #1de9b6;
120
+ box-shadow: 0 0 0 2px #1de9b688;
121
+ }
122
+ .signin-field input::placeholder {
123
+ color: #b0b8c1;
124
+ opacity: 1;
125
+ }
126
+ .signin-field small.error {
127
+ color: #ff5252;
128
+ font-size: 0.85rem;
129
+ margin-top: 4px;
130
+ }
131
+ .signin-options-row {
132
+ display: flex;
133
+ justify-content: space-between;
134
+ align-items: center;
135
+ margin-bottom: 2vw;
136
+ margin-top: -8px;
137
+ }
138
+ .remember-me {
139
+ display: flex;
140
+ align-items: center;
141
+ gap: 6px;
142
+ font-size: 1rem;
143
+ color: #b0b8c1;
144
+ }
145
+ .remember-me input[type="checkbox"] {
146
+ accent-color: #38bdf8;
147
+ width: 16px;
148
+ height: 16px;
149
+ }
150
+ .forgot-password {
151
+ font-size: 1rem;
152
+ }
153
+ .forgot-link {
154
+ color: #38bdf8;
155
+ text-decoration: underline;
156
+ font-weight: 500;
157
+ cursor: pointer;
158
+ }
159
+ .signin-btn {
160
+ width:100%;
161
+ background: #18314a;
162
+ color: #fff;
163
+ border: none;
164
+ border-radius: 8px;
165
+ padding: 14px 0;
166
+ font-size: 1.1rem;
167
+ font-weight: 700;
168
+ margin-bottom: 18px;
169
+ cursor: pointer;
170
+ transition: background 0.2s, color 0.2s;
171
+ }
172
+ .signin-btn:hover {
173
+ background: #38bdf8;
174
+ }
175
+ .signin-footer {
176
+ color: #b0b8c1;
177
+ font-size: 0.95rem;
178
+ text-align: center;
179
+ margin-top: 8px;
180
+ }
181
+ .signin-footer .footer-sep {
182
+ display: block;
183
+ height: 16px;
184
+ }
185
+ .signin-footer a {
186
+ color: #38bdf8;
187
+ text-decoration: underline;
188
+ margin-left: 4px;
189
+ font-weight: 600;
190
+ cursor: pointer;
191
+ }
192
+ .signin-close {
193
+ position: absolute;
194
+ top: 18px;
195
+ right: 18px;
196
+ width: 38px;
197
+ height: 38px;
198
+ border: none;
199
+ background: #14263c;
200
+ color: #fff;
201
+ border-radius: 50%;
202
+ font-size: 2rem;
203
+ font-weight: bold;
204
+ display: flex;
205
+ align-items: center;
206
+ justify-content: center;
207
+ cursor: pointer;
208
+ z-index: 10;
209
+ transition: background 0.2s, color 0.2s;
210
+ box-shadow: 0 2px 8px #0005;
211
+ }
212
+ .signin-close:hover {
213
+ background: #38bdf8;
214
+ color: #18314a;
215
+ }
216
+
217
+ @media (max-width: 700px) {
218
+ .signin-box {
219
+ padding: 18px 6vw 18px 6vw;
220
+ width: 98vw;
221
+ }
222
+ .signin-header {
223
+ flex-direction: column;
224
+ align-items: flex-start;
225
+ width: 98vw;
226
+ padding: 0 0 12px 0;
227
+ }
228
+ .signin-title {
229
+ font-size: 1.3rem;
230
+ }
231
+ .signin-row {
232
+ flex-direction: column;
233
+ gap: 0;
234
+ }
235
+ }
236
+ .ai-particle-bg {
237
+ position: absolute;
238
+ inset: 0;
239
+ pointer-events: none;
240
+ z-index: 0;
241
+ background: url('/assets/particles.svg');
242
+ opacity: 0.18;
243
+ animation: particleDrift 18s linear infinite;
244
+ }
245
+ @keyframes particleDrift {
246
+ 0% { background-position: 0 0; }
247
+ 100% { background-position: 120px 80px; }
248
+ }
249
+ .spinner {
250
+ display: inline-block;
251
+ width: 18px;
252
+ height: 18px;
253
+ border: 3px solid #fff;
254
+ border-top: 3px solid #38bdf8;
255
+ border-radius: 50%;
256
+ animation: spin 0.7s linear infinite;
257
+ vertical-align: middle;
258
+ margin-right: 8px;
259
+ }
260
+
261
+ @keyframes spin {
262
+ 0% { transform: rotate(0deg);}
263
+ 100% { transform: rotate(360deg);}
264
+ }
265
+
266
+ .py-logo-glow {
267
+ width: 54px;
268
+ height: 54px;
269
+ border-radius: 50%;
270
+ box-shadow: 0 0 24px #38bdf8, 0 0 12px #13bfa6;
271
+ animation: logoGlow 3.5s ease-in-out infinite alternate;
272
+ }
273
+ @keyframes logoGlow {
274
+ 0% { box-shadow: 0 0 12px #38bdf8, 0 0 6px #13bfa6; }
275
+ 100% { box-shadow: 0 0 32px #38bdf8, 0 0 18px #13bfa6; }
276
+ }
277
+ .ai-scan-line {
278
+ display: none;
279
+ }
280
+ .ai-slide-in {
281
+ animation: slideInBox 0.8s cubic-bezier(.39,.58,.57,1) both;
282
+ }
283
+ @keyframes slideInBox {
284
+ 0% { opacity: 0; transform: translateY(40px) scale(0.98); }
285
+ 100% { opacity: 1; transform: translateY(0) scale(1); }
286
+ }
287
+ .lock-icon {
288
+ color: #38bdf8;
289
+ font-size: 1.2em;
290
+ margin-right: 8px;
291
+ vertical-align: middle;
292
+ }
293
+ .signin-tagline {
294
+ color: #b0b8c1;
295
+ font-size: 1.08em;
296
+ text-align: center;
297
+ margin-bottom: 8px;
298
+ font-weight: 600;
299
+ }
300
+ .signin-welcome {
301
+ color: #38bdf8;
302
+ font-size: 1.05em;
303
+ text-align: center;
304
+ margin-bottom: 18px;
305
+ font-weight: 600;
306
+ }
307
+ .switch {
308
+ position: relative;
309
+ display: inline-block;
310
+ width: 38px;
311
+ height: 22px;
312
+ }
313
+ .switch input {
314
+ opacity: 0;
315
+ width: 0;
316
+ height: 0;
317
+ }
318
+ .slider {
319
+ position: absolute;
320
+ cursor: pointer;
321
+ top: 0; left: 0; right: 0; bottom: 0;
322
+ background: #b0b8c1;
323
+ border-radius: 22px;
324
+ transition: .4s;
325
+ }
326
+ .switch input:checked + .slider {
327
+ background: #38bdf8;
328
+ }
329
+ .slider:before {
330
+ position: absolute;
331
+ content: "";
332
+ height: 16px;
333
+ width: 16px;
334
+ left: 3px;
335
+ bottom: 3px;
336
+ background: #fff;
337
+ border-radius: 50%;
338
+ transition: .4s;
339
+ }
340
+ .switch input:checked + .slider:before {
341
+ transform: translateX(16px);
342
+ }
343
+ .signin-hint {
344
+ color: #b0b8c1;
345
+ font-size: 0.95em;
346
+ text-align: right;
347
+ margin-bottom: 8px;
348
+ }
349
+ .ai-pulse {
350
+ animation: aiPulseGlow 1.5s infinite alternate;
351
+ }
352
+ @keyframes aiPulseGlow {
353
+ 0% { box-shadow: 0 2px 12px #38bdf844; }
354
+ 100% { box-shadow: 0 2px 24px #38bdf888; }
355
+ }
356
+ .signin-session-tip {
357
+ color: #b0b8c1;
358
+ font-size: 0.95em;
359
+ text-align: center;
360
+ margin-bottom: 8px;
361
+ }
362
+ .signin-security-note {
363
+ color: #ff5252;
364
+ font-size: 0.95em;
365
+ font-weight: 600;
366
+ display: block;
367
+ margin-bottom: 4px;
368
+ }
369
+ .signin-error-toast {
370
+ background: transparent;
371
+ color: #ff5252;
372
+ font-weight: 700;
373
+ border-radius: 8px;
374
+ border: 1px solid #ff5252;
375
+ padding: 8px 18px;
376
+ margin: 12px 0;
377
+ text-align: center;
378
+ animation: shakeError 0.3s cubic-bezier(.39,.58,.57,1);
379
+ }
380
+ @keyframes shakeError {
381
+ 0% { transform: translateX(0); }
382
+ 20% { transform: translateX(-8px); }
383
+ 40% { transform: translateX(8px); }
384
+ 60% { transform: translateX(-8px); }
385
+ 80% { transform: translateX(8px); }
386
+ 100% { transform: translateX(0); }
387
+ }
388
+ .forgot-modal-bg {
389
+ position: fixed;
390
+ inset: 0;
391
+ background: rgba(30,41,59,0.9);
392
+ z-index: 2000;
393
+ display: flex;
394
+ align-items: center;
395
+ justify-content: center;
396
+ animation: fadeInModalBg 0.4s;
397
+ }
398
+ @keyframes fadeInModalBg {
399
+ from { opacity: 0; }
400
+ to { opacity: 1; }
401
+ }
402
+ .forgot-modal {
403
+ background: #fff;
404
+ border-radius: 18px;
405
+ box-shadow: 0 8px 32px #38bdf844, 0 0 24px #1e293b88;
406
+ padding: 32px 36px 28px 36px;
407
+ min-width: 320px;
408
+ max-width: 90vw;
409
+ text-align: center;
410
+ z-index: 2001;
411
+ display: flex;
412
+ flex-direction: column;
413
+ align-items: center;
414
+ animation: fadeInModal 0.4s;
415
+ }
416
+ @keyframes fadeInModal {
417
+ from { opacity: 0; transform: scale(0.98); }
418
+ to { opacity: 1; transform: scale(1); }
419
+ }
420
+ .forgot-modal h3 {
421
+ color: #38bdf8;
422
+ margin: 12px 0 8px 0;
423
+ font-size: 1.4em;
424
+ font-weight: 700;
425
+ }
426
+ .forgot-modal p {
427
+ color: #23272b;
428
+ font-size: 1.08em;
429
+ margin-bottom: 18px;
430
+ }
431
+ .forgot-modal input[type="email"] {
432
+ background: #f4f6fa;
433
+ color: #18314a;
434
+ border: none;
435
+ border-radius: 8px;
436
+ padding: 12px 14px;
437
+ font-size: 1rem;
438
+ margin-bottom: 12px;
439
+ box-shadow: 0 1px 4px #0002;
440
+ width: 100%;
441
+ }
442
+ .modal-close {
443
+ width: 23%;
444
+ background: #18314a;
445
+ color: #fff;
446
+ border: none;
447
+ border-radius: 8px;
448
+ padding: 14px 0;
449
+ font-size: 1.1rem;
450
+ font-weight: 700;
451
+ margin-bottom: 0;
452
+ cursor: pointer;
453
+ transition: background 0.2s, color 0.2s;
454
+ display: block;
455
+ }
456
+
457
+ .modal-close:hover {
458
+ background: #38bdf8;
459
+ color: #18314a;
460
+ }
461
+ .google-signin-row {
462
+ width: 100%;
463
+ display: flex;
464
+ justify-content: center;
465
+ margin-bottom: 12px;
466
+ }
467
+ #google-btn-div {
468
+ width: 100%;
469
+ display: flex;
470
+ justify-content: center;
471
+ }
472
+ #google-btn-div > div {
473
+ width: 100%;
474
+ min-width: 0;
475
+ border-radius: 8px !important;
476
+ padding: 14px 0 !important;
477
+ font-size: 1.1rem !important;
478
+ font-weight: 700 !important;
479
+ box-shadow: 0 2px 8px #0003 !important;
480
+ background: #fff !important;
481
+ color: #18314a !important;
482
+ border: none !important;
483
+ margin: 0 !important;
484
+ display: flex;
485
+ align-items: center;
486
+ justify-content: center;
487
+ }
488
+ .g-signin2 {
489
+ width: 100%;
490
+ min-width: 0;
491
+ border-radius: 8px !important;
492
+ padding: 14px 0 !important;
493
+ font-size: 1.1rem !important;
494
+ font-weight: 700 !important;
495
+ box-shadow: 0 2px 8px #0003 !important;
496
+ background: #fff !important;
497
+ color: #18314a !important;
498
+ border: none !important;
499
+ margin: 0 !important;
500
+ display: flex;
501
+ align-items: center;
502
+ justify-content: center;
503
+ }
504
+
505
+ .eye-toggle {
506
+ position: absolute;
507
+ right: 12px;
508
+ top: 50%;
509
+ transform: translateY(-50%);
510
+ background: none;
511
+ border: none;
512
+ font-size: 1.3em;
513
+ color: #888;
514
+ cursor: pointer;
515
+ z-index: 2;
516
+ padding: 0;
517
+ line-height: 1;
518
+ opacity: 0.7;
519
+ transition: color 0.2s, opacity 0.2s;
520
+ }
521
+ #password {
522
+ padding-right: 40px;
523
+ }
524
+
525
+ .password-label {
526
+ display: inline-flex;
527
+ align-items: center;
528
+ gap: 8px;
529
+ }
530
+ .password-label .eye-toggle {
531
+ position: static;
532
+ right: auto;
533
+ top: auto;
534
+ background: none;
535
+ border: none;
536
+ font-size: 1rem;
537
+ color: #888;
538
+ opacity: 0.9;
539
+ padding: 0 4px;
540
+ }
541
+ .password-label .eye-toggle:hover {
542
+ color: #555;
543
+ opacity: 1;
544
+ }
545
+
546
+ /* --- UI DESIGN UPDATE TO MATCH SCREENSHOT --- */
547
+ .auth-box {
548
+ display: grid;
549
+ grid-template-columns: 1fr 1fr;
550
+ width: 820px;
551
+ max-width: 98vw;
552
+ min-width: 340px;
553
+ border-radius: 18px;
554
+ box-shadow: 0 8px 32px #0002;
555
+ overflow: hidden;
556
+ background: none;
557
+ }
558
+
559
+ .panel-left {
560
+ background: #fff;
561
+ color: #222;
562
+ display: flex;
563
+ flex-direction: column;
564
+ align-items: center;
565
+ justify-content: center;
566
+ padding: 48px 32px;
567
+ }
568
+ .panel-left .signin-title {
569
+ font-size: 2.1rem;
570
+ font-weight: 800;
571
+ margin-bottom: 12px;
572
+ text-align: center;
573
+ color: #222;
574
+ }
575
+ .panel-left .signin-desc {
576
+ font-size: 1.05rem;
577
+ margin-bottom: 24px;
578
+ text-align: center;
579
+ color: #555;
580
+ }
581
+ .panel-left .signin-btn {
582
+ background: var(--primary-cyan-mid);
583
+ color: #fff;
584
+ border: none;
585
+ border-radius: 999px;
586
+ padding: 12px 32px;
587
+ font-size: 1.1rem;
588
+ font-weight: 700;
589
+ cursor: pointer;
590
+ transition: background 0.2s, color 0.2s;
591
+ box-shadow: 0 2px 8px rgba(0,0,0,0.2);
592
+ }
593
+ .panel-left .signin-btn:hover {
594
+ background: var(--primary-cyan-dark);
595
+ color: #fff;
596
+ }
597
+
598
+ .panel-right {
599
+ background: linear-gradient(135deg, var(--primary-cyan-dark) 0%, var(--primary-cyan-mid) 100%);
600
+ color: #fff;
601
+ display: flex;
602
+ flex-direction: column;
603
+ align-items: center;
604
+ justify-content: center;
605
+ padding: 48px 32px;
606
+ }
607
+ .panel-right .signup-title {
608
+ font-size: 2rem;
609
+ font-weight: 800;
610
+ margin-bottom: 18px;
611
+ text-align: left;
612
+ color: #fff;
613
+ }
614
+ .panel-right .signup-desc {
615
+ font-size: 1.05rem;
616
+ margin-bottom: 24px;
617
+ text-align: center;
618
+ color: #fff;
619
+ }
620
+ .panel-right .signup-btn {
621
+ background: none;
622
+ color: #fff;
623
+ border: 2px solid #fff;
624
+ border-radius: 999px;
625
+ padding: 12px 32px;
626
+ font-size: 1.1rem;
627
+ font-weight: 700;
628
+ cursor: pointer;
629
+ transition: background 0.2s, color 0.2s;
630
+ box-shadow: none;
631
+ }
632
+
633
+
634
+ /* Input fields and buttons for left panel */
635
+ .panel-left .signin-field input,
636
+ .panel-left .signin-field select {
637
+ background: #f5f5f5;
638
+ color: #222;
639
+ border: 1px solid #ddd;
640
+ border-radius: 8px;
641
+ padding: 12px 14px;
642
+ font-size: 1rem;
643
+ margin-bottom: 2px;
644
+ box-shadow: 0 1px 4px #0001;
645
+ transition: border 0.2s, box-shadow 0.2s;
646
+ }
647
+ .panel-left .signin-field input:focus,
648
+ .panel-left .signin-field select:focus {
649
+ outline: 2px solid #ff416c;
650
+ border-color: #ff416c;
651
+ box-shadow: 0 0 0 2px #ff416c44;
652
+ }
653
+
654
+ /* Social buttons row for left panel */
655
+ .panel-left .social-row {
656
+ display: flex;
657
+ gap: 12px;
658
+ margin-bottom: 18px;
659
+ justify-content: center;
660
+ }
661
+ .panel-left .social-btn {
662
+ width: 38px;
663
+ height: 38px;
664
+ border-radius: 50%;
665
+ background: #f5f5f5;
666
+ display: flex;
667
+ align-items: center;
668
+ justify-content: center;
669
+ font-size: 1.3em;
670
+ color: var(--primary-cyan-dark);
671
+ border: none;
672
+ cursor: pointer;
673
+ box-shadow: 0 2px 8px #0001;
674
+ transition: background 0.2s, color 0.2s;
675
+ }
676
+ .panel-left .social-btn:hover {
677
+ background: var(--primary-cyan-dark);
678
+ color: #fff;
679
+ }
680
+
681
+ /* Professional shadow and rounded corners for the whole box */
682
+ .auth-box {
683
+ box-shadow: 0 8px 32px #0002;
684
+ border-radius: 18px;
685
+ }
686
+
687
+ /* Ensure identical size and prevent clipping */
688
+ .auth-box {
689
+ display: grid;
690
+ grid-template-columns: 420px 600px; /* match sign-up columns */
691
+ width: 1020px; /* increased to accommodate both panels */
692
+ height: 620px; /* increased height to avoid overflow */
693
+ max-width: 98vw;
694
+ min-width: 340px;
695
+ border-radius: 18px;
696
+ box-shadow: 0 8px 32px #0002;
697
+ overflow: visible; /* ensure no content is clipped */
698
+ background: none;
699
+ position: relative;
700
+ box-sizing: border-box;
701
+ }
702
+
703
+ /* Reduce right panel width to make it less large */
704
+ .auth-box {
705
+ display: grid;
706
+ grid-template-columns: 360px 840px; /* reduced right panel from 600px to 520px */
707
+ width: 850px; /* adjusted total width to match columns */
708
+ height: 820px;
709
+ max-width: 98vw;
710
+ min-width: 340px;
711
+ border-radius: 18px;
712
+ box-shadow: 0 8px 32px #0002;
713
+ overflow: visible; /* ensure no content is clipped */
714
+ background: none;
715
+ position: relative;
716
+ box-sizing: border-box;
717
+ justify-content:end;
718
+ align-items:baseline;
719
+ justify-items:start;
720
+ }
721
+
722
+ /* Ensure panels occupy full height and use box-sizing */
723
+ .panel-left, .panel-right {
724
+ height: 100%;
725
+ box-sizing: border-box;
726
+ }
727
+
728
+ /* Card viewport and sliding inner container */
729
+ .auth-card {
730
+ width: 1140px; /* viewport width remains */
731
+ height: 700px; /* increased height to accommodate content and remove scroll */
732
+ perspective: none;
733
+ overflow: hidden;
734
+ border-radius: 12px;
735
+ box-shadow: 0 8px 24px rgba(0,0,0,0.12), #ffffff 1px 1px 51px; /* added white glow */
736
+ margin: 0 auto;
737
+ }
738
+
739
+ .card-inner {
740
+ width:200%; /* contains both faces side-by-side */
741
+ height:100%;
742
+ display: flex; /* place front and back side-by-side */
743
+ /* much slower transition so flip is clearly visible to users */
744
+ transition: transform 0.7s cubic-bezier(.22, .9, .32,1);
745
+ will-change: transform;
746
+ }
747
+
748
+ .auth-card.flipped .card-inner {
749
+ transform: translate3d(-50%,0,0);
750
+ }
751
+ .auth-card:not(.flipped) .card-inner {
752
+ transform: translate3d(0,0,0);
753
+ }
754
+
755
+ /* panel fade with proper syntax */
756
+ .card-front .main-panel,
757
+ .card-back .main-panel {
758
+ transition: opacity 1s ease 0.15s;
759
+ }
760
+ .card-front[aria-hidden="true"] .main-panel,
761
+ .card-back[aria-hidden="true"] .main-panel {
762
+ opacity:0;
763
+ }
764
+ .card-front[aria-hidden="false"] .main-panel,
765
+ .card-back[aria-hidden="false"] .main-panel {
766
+ opacity:1;
767
+ }
768
+
769
+ /* faces: each takes 50% of the inner width (i.e. the viewport) */
770
+ .card-front, .card-back {
771
+ width: 50%;
772
+ height: 100%;
773
+ flex: 0 0 50%;
774
+ box-sizing: border-box;
775
+ position: relative; /* allow absolute children inside */
776
+ overflow: hidden;
777
+ }
778
+
779
+ /* layout inside each face: side-panel + main-panel split */
780
+ .card-content {
781
+ display: flex;
782
+ height: 100%;
783
+ flex-direction: row-reverse;
784
+ }
785
+ .side-panel { width: 48%; display:flex; align-items:center; justify-content:center; }
786
+ .main-panel { width: 55%; padding: 48px 48px; box-sizing:border-box; background:#fff; overflow: visible; display:flex; flex-direction:column; align-items:center; }
787
+
788
+ /* center logo header */
789
+ .logo-header {
790
+ display: flex;
791
+ flex-direction: column;
792
+ align-items: center;
793
+ justify-content: center;
794
+ text-align: center;
795
+ margin-bottom: 8px;
796
+ }
797
+ .py-learn-text {
798
+ font-size: 3vw;
799
+ font-weight: 600;
800
+ color: #073879;
801
+ font-family: Amonk_Outline;
802
+ margin-bottom: 1vw;
803
+ }
804
+ .self-learning-system {
805
+ font-size: 1.2vw;
806
+ font-weight: bolder;
807
+ color: #073879;
808
+ margin-top: -.8vw;
809
+ }
810
+
811
+ .side-right {
812
+ background: linear-gradient(135deg,#137ec4 0%,#137ec4 100%);
813
+ }
814
+ .side-left {
815
+ background: linear-gradient(135deg, #137ec4 0%, #38bdf8 100%);
816
+ }
817
+ .side-inner { color:#fff; text-align:center; padding: 32px; }
818
+ .side-inner h3 { font-size: 1.6rem; margin-bottom:12px; }
819
+ .side-text { opacity: 0.95; }
820
+ .panel-cta { background:none; border:2px solid #fff; color:#fff; padding:10px 22px; border-radius:999px; cursor:pointer; margin-top:12px; }
821
+
822
+ /* Ensure sign-in text colors for white main panel */
823
+ .card-front .main-panel .signin-title { color: #222; }
824
+ .card-back .main-panel .signup-title { color: #222; }
825
+
826
+ /* Maintain previous smaller-screen behavior (stack panels) */
827
+ @media (max-width: 900px){
828
+ .auth-card{ width:92vw; height:auto; }
829
+ .card-inner{ width: 200%; /* still double width but we'll stack */ }
830
+ .card-content{ flex-direction: column; }
831
+ .side-panel{ width:100%; height:200px; }
832
+ .main-panel{ width:100%; overflow: visible; }
833
+ }
834
+
835
+ /* keep existing control styles unchanged (inputs/buttons/etc.) */
836
+
837
+ /* minimal overwrite of previous rules that used absolute positioning */
838
+ .m, .signin-close, .card-back .signin-close { position: absolute; top: 5px; right: 5px; z-index: 10; }
839
+
840
+ /* accessibility: hide offscreen face from assistive tech when sliding */
841
+ .card-front[aria-hidden="true"], .card-back[aria-hidden="true"] {
842
+ pointer-events: none;
843
+ }
844
+
845
+ /* Change right panel gradient to blue/cyan */
846
+ .side-panel.side-right {
847
+ background: linear-gradient(135deg, #1d608b 0%, #1d608b 100%);
848
+ }
849
+
850
+ /* Info box styling for right panel */
851
+ .side-info-box {
852
+ position: absolute;
853
+ top: 164px;
854
+ left: 0;
855
+ width: 88%;
856
+ padding: 0 32px;
857
+ z-index: 2;
858
+ text-align: left;
859
+ }
860
+ .side-info-title {
861
+ font-size: 1.35rem;
862
+ font-weight: 800;
863
+ color: #fff;
864
+ margin-bottom: 6px;
865
+ letter-spacing: 0.5px;
866
+ }
867
+ .side-info-desc {
868
+ font-size: 1.08rem;
869
+ color: #e0f7fa;
870
+ margin-bottom: 8px;
871
+ }
872
+ .side-info-link {
873
+ color: #fff;
874
+ font-weight: 700;
875
+ text-decoration: underline;
876
+ cursor: pointer;
877
+ transition: color 0.2s;
878
+ }
879
+ .side-info-link:hover {
880
+ color: #23395d;
881
+ }
882
+
883
+ /* Ensure info box is above image */
884
+ .side-panel {
885
+ position: relative;
886
+ }
887
+
888
+ /* Google button row spacing */
889
+ .google-signin-row {
890
+ width: 100%;
891
+ display: flex;
892
+ justify-content: center;
893
+ margin-bottom: 12px;
894
+ }
895
+ #google-btn-div {
896
+ width: 100%;
897
+ display: flex;
898
+ justify-content: center;
899
+ }
900
+ .g-signin2 {
901
+
902
+ margin: 0 auto;
903
+ }
904
+
905
+ /* Ensure main-panel text is black (readable on white background) */
906
+ .card-front .main-panel,
907
+ .card-front .main-panel .signin-title,
908
+ .card-front .main-panel .signin-tagline,
909
+ .card-front .main-panel .signin-welcome,
910
+ .card-front .main-panel .signin-session-tip,
911
+ .card-front .main-panel .signin-hint,
912
+ .card-front .main-panel .signin-footer,
913
+ .card-front .main-panel label,
914
+ .card-front .main-panel .signin-field small,
915
+ .card-front .main-panel .signin-field label {
916
+ color: #23395d !important;
917
+ }
918
+
919
+ /* Ensure required field errors are red on the front main panel */
920
+ .card-front .main-panel .signin-field small.error {
921
+ color: #ff5252 !important;
922
+ }
923
+
924
+ /* Keep links styled but darken slightly for contrast */
925
+ .card-front .main-panel a {
926
+ color: #0b57a4 !important;
927
+ }
928
+ .signin-divider-row {
929
+ display: flex;
930
+ align-items: center;
931
+ justify-content: center;
932
+ width:100%;
933
+ margin:12px 018px 0;
934
+ }
935
+ .divider {
936
+ flex:1;
937
+ height:1px;
938
+ background: #b0b8c1;
939
+ margin:08px;
940
+ }
941
+ .divider-or {
942
+ color: #23395d;
943
+ font-size:1.08em;
944
+ font-weight:600;
945
+ margin:08px;
946
+ }
947
+ .google-btn {
948
+ width: 100%;
949
+ height: 45px;
950
+ background: #18314a;
951
+ color: #fff;
952
+ border: none;
953
+ border-radius: 8px;
954
+ padding: 14px 0;
955
+ font-size: 1.1rem;
956
+ font-weight: 700;
957
+ margin-bottom: 18px;
958
+ cursor: pointer;
959
+ box-shadow: 02px 8px #0003;
960
+ display: flex;
961
+ align-items: center;
962
+ justify-content: center;
963
+ gap: 12px;
964
+ transition: background 0.2s, color 0.2s;
965
+ }
966
+ .google-btn:hover {
967
+ background: #38bdf8;
968
+ }
969
+ .google-logo {
970
+ width:24px;
971
+ height:24px;
972
+ }
973
+ .fact-rotator {
974
+ font-size:1.18rem;
975
+ font-weight:700;
976
+ color: #fff;
977
+ margin-bottom:8px;
978
+ min-height:32px;
979
+ text-align: left;
980
+ transition: opacity 0.6s;
981
+ letter-spacing:0.5px;
982
+ animation: fadeFact0.6s;
983
+ }
984
+ @keyframes fadeFact {
985
+ from { opacity:0; }
986
+ to { opacity:1; }
987
+ }
988
+ .side-panel.side-left {
989
+ position: relative;
990
+ overflow: hidden;
991
+ background: #1d608b; /* Strong blue for contrast */
992
+ min-height: 400px;
993
+ }
994
+ .side-img {
995
+ position: absolute;
996
+ top:0; left:0;
997
+ width:100%; height:100%;
998
+ object-fit: cover;
999
+ z-index:0;
1000
+ }
1001
+ .side-bg-shapes {
1002
+ position: absolute;
1003
+ top:0; left:0; right:0; bottom:0;
1004
+ width:100%; height:100%;
1005
+ z-index:1;
1006
+ pointer-events: none;
1007
+ }
1008
+ .bg-circle {
1009
+ position: absolute;
1010
+ border-radius:50%;
1011
+ opacity:0.85;
1012
+ }
1013
+ .circle1 {
1014
+ width: 120px;
1015
+ height: 120px;
1016
+ top: 18px;
1017
+ left: 18px;
1018
+ background: linear-gradient(135deg, #38bdf8 80%, #137ec4 100%);
1019
+ }
1020
+ .circle2 {
1021
+ width: 80px;
1022
+ height: 80px;
1023
+ top: 71%;
1024
+ left: 113px;
1025
+ background: linear-gradient(135deg, #38bdf8 80%, #137ec4 100%);
1026
+ }
1027
+
1028
+ .circle3 {
1029
+ width: 48px;
1030
+ height: 48px;
1031
+ top: 80%;
1032
+ left: 70%;
1033
+ background: linear-gradient(135deg, #38bdf8 80%, #137ec4 100%);
1034
+ }
1035
+
1036
+ .circle4 {
1037
+ width: 160px;
1038
+ height: 160px;
1039
+ top: 70px;
1040
+ right: 204px;
1041
+ background: linear-gradient(135deg, #38bdf8 80%, #137ec4 100%);
1042
+ opacity: 0.7;
1043
+ }
1044
+
1045
+ .circle5 {
1046
+ width: 36px;
1047
+ height: 36px;
1048
+ top: 30%;
1049
+ left: 80%;
1050
+ background: linear-gradient(135deg, #38bdf8 80%, #137ec4 100%);
1051
+ opacity: 0.6;
1052
+ }
1053
+
1054
+ .circle6 {
1055
+ width: 60px;
1056
+ height: 60px;
1057
+ bottom: 12%;
1058
+ right: 18%;
1059
+ background: linear-gradient(135deg, #38bdf8 80%, #137ec4 100%);
1060
+ opacity: 0.5;
1061
+ }
1062
+
1063
+ .circle7 {
1064
+ width: 54px;
1065
+ height: 54px;
1066
+ top: 10%;
1067
+ right: 10%;
1068
+ background: #18314a;
1069
+ opacity: 0.8;
1070
+ }
1071
+
1072
+ .circle8 {
1073
+ width: 32px;
1074
+ height: 32px;
1075
+ bottom: 20%;
1076
+ left: 60%;
1077
+ background: #14263c;
1078
+ opacity: 0.8;
1079
+ }
1080
+
1081
+ .circle9 {
1082
+ width: 44px;
1083
+ height: 44px;
1084
+ top: 75%;
1085
+ left: 40%;
1086
+ background: #18314a;
1087
+ opacity: 0.7;
1088
+ }
1089
+
1090
+ .circle10 {
1091
+ width: 28px;
1092
+ height: 28px;
1093
+ top: 15%;
1094
+ left: 60%;
1095
+ background: #14263c;
1096
+ opacity: 0.9;
1097
+ }
1098
+
1099
+ .circle11 {
1100
+ width: 38px;
1101
+ height: 38px;
1102
+ bottom: 10%;
1103
+ right: 10%;
1104
+ background: #18314a;
1105
+ opacity: 0.7;
1106
+ }
1107
+
1108
+ .circle12 {
1109
+ width: 22px;
1110
+ height: 22px;
1111
+ top: 85%;
1112
+ right: 20%;
1113
+ background: #14263c;
1114
+ opacity: 0.8;
1115
+ }
1116
+
1117
+ /* New large solid circles for sign-in page background */
1118
+ .circle-large1 {
1119
+ width:220px;
1120
+ height:220px;
1121
+ top:40px;
1122
+ left:60px;
1123
+ background: #137ec4;
1124
+ opacity:0.45;
1125
+ z-index:0;
1126
+ }
1127
+ .circle-large2 {
1128
+ width:180px;
1129
+ height:180px;
1130
+ bottom:40px;
1131
+ right:40px;
1132
+ background: #38bdf8;
1133
+ opacity:0.35;
1134
+ z-index:0;
1135
+ }
1136
+ .circle-large3 {
1137
+ width:140px;
1138
+ height:140px;
1139
+ top:60%;
1140
+ left:60%;
1141
+ background: #18314a;
1142
+ opacity:0.25;
1143
+ z-index:0;
1144
+ }
1145
+ .circle-large4 {
1146
+ width:100px;
1147
+ height:100px;
1148
+ bottom:11%;
1149
+ left:5%;
1150
+ background: #13bfa6;
1151
+ opacity:0.52;
1152
+ z-index:0;
1153
+ }
1154
+
1155
+ /* Add more white rings for contrast */
1156
+ .bg-ring {
1157
+ position: absolute;
1158
+ border-radius:50%;
1159
+ border:6px solid #fff;
1160
+ background: none;
1161
+ opacity:0.8;
1162
+ z-index:1;
1163
+ }
1164
+ .ring1 { width:90px; height:90px; bottom:24px; left:18px; }
1165
+ .ring2 { width:48px; height:48px; top:60px; right:32px; }
1166
+ .ring3 { width:72px; height:72px; top:10%; left:70%; border-color: #fff; opacity:0.7; }
1167
+ .ring4 { width:110px; height:110px; bottom:2%; right:43%; border-color: #fff; opacity:0.5; }
1168
+ .ring5 { width:38px; height:38px; top:75%; left:10%; border-color: #fff; opacity:0.8; }
1169
+ .ring6 { width:64px; height:64px; top:49%; right:20%; border-color: #fff; opacity:0.7; }
1170
+ .ring7 { width:120px; height:120px; top:14%; left:49%; border-color: #fff; opacity:0.6; }
1171
+ .ring8 { width:80px; height:80px; bottom:20%; right:30%; border-color: #fff; opacity:0.7; }
1172
+ .ring9 { width:60px; height:60px; top:65%; left:36%; border-color: #fff; opacity:0.7; }
1173
+
1174
+ .side-welcome-overlay {
1175
+ position: absolute;
1176
+ top:30%;
1177
+ left:50px;
1178
+ width:93%;
1179
+ text-align: start;
1180
+ z-index:2;
1181
+ padding:024px;
1182
+ pointer-events: auto;
1183
+ }
1184
+ .welcome-back-title {
1185
+ font-size:2.1rem;
1186
+ font-weight:800;
1187
+ color: #fff;
1188
+ margin-bottom:8px;
1189
+
1190
+ }
1191
+ .welcome-back-desc {
1192
+ font-size:1rem;
1193
+ color: #e0f7fa;
1194
+ margin-bottom:18px;
1195
+
1196
+ }
1197
+ .action-btn {
1198
+ width:21%;
1199
+ background: #18314a;
1200
+ color: #fff;
1201
+ border: none;
1202
+ border-radius:8px;
1203
+ padding:14px 0;
1204
+ font-size:1.1rem;
1205
+ font-weight:700;
1206
+ margin-top:18px;
1207
+ margin-bottom:0;
1208
+ display: inline-block;
1209
+ letter-spacing:0.5px;
1210
+ box-shadow:02px 8px #0003;
1211
+ cursor: pointer;
1212
+ transition: background 0.2s, color 0.2s;
1213
+ }
1214
+ .action-btn:hover {
1215
+ background: #38bdf8;
1216
+ color: #18314a;
1217
+ }
1218
+
1219
+ /* Place reveal icon inline when inside the password label */
1220
+ .password-label {
1221
+ display: inline-flex;
1222
+ align-items: center;
1223
+ gap: 8px;
1224
+ }
1225
+
1226
+ /* Wrap for input with trailing icon */
1227
+ .input-with-icon {
1228
+ position: relative;
1229
+ display: flex;
1230
+ align-items: center;
1231
+ }
1232
+ .input-with-icon input {
1233
+ width: 100%;
1234
+ padding-right: 42px; /* space for the icon */
1235
+ }
1236
+ .input-with-icon .eye-toggle {
1237
+ position: absolute;
1238
+ right: 12px;
1239
+ top: 50%;
1240
+ transform: translateY(-50%);
1241
+ background: none;
1242
+ border: none;
1243
+ font-size: 1.2em;
1244
+ color: #888;
1245
+ cursor: pointer;
1246
+ line-height: 1;
1247
+ padding: 0;
1248
+ opacity: 0.8;
1249
+ }
1250
+ .input-with-icon .eye-toggle:hover {
1251
+ opacity: 1;
1252
+ color: #555;
1253
+ }
1254
+
1255
+ /* add invalid input red outline */
1256
+ .signin-field input.invalid {
1257
+ border: 1px solid #ff5252;
1258
+ }
1259
+
1260
+ /* === Readability enhancements for left image overlay (scoped) === */
1261
+ .side-panel.side-left { position: relative; }
1262
+ .side-panel.side-left::before {
1263
+ content: "";
1264
+ position: absolute;
1265
+ inset: 0;
1266
+ background: linear-gradient(205deg, rgba(5,25,46,0.85) 8%, rgba(5,25,46,0.70) 40%, rgba(5,25,46,0.35) 75%);
1267
+ z-index: 1; /* sits above the image, below text card */
1268
+ pointer-events: none;
1269
+ }
1270
+ /* Frosted glass card for the welcome text */
1271
+ .side-panel.side-left .side-welcome-overlay {
1272
+ position: absolute;
1273
+ top: 44%;
1274
+ left: 10%;
1275
+ z-index: 2;
1276
+ background: rgba(15, 40, 70, 0.55);
1277
+ backdrop-filter: blur(6px) saturate(140%);
1278
+ -webkit-backdrop-filter: blur(6px) saturate(140%);
1279
+ padding: 20px 24px 24px;
1280
+ border-radius: 18px;
1281
+ border: 1px solid rgba(255, 255, 255, 0.15);
1282
+ box-shadow: 0 4px 20px -4px rgba(0, 0, 0, 0.55), 0 0 0 1px rgba(255, 255, 255, 0.05);
1283
+ max-width: 429px;
1284
+ width: auto;
1285
+
1286
+ text-align: center;
1287
+ }
1288
+ /* Strengthen text contrast */
1289
+ .side-panel.side-left .welcome-back-desc,
1290
+ .side-panel.side-left .welcome-back-title { text-shadow: 0 2px 6px rgba(0,0,0,0.6); }
1291
+ /* Refine button inside overlay only */
1292
+ .side-panel.side-left .action-btn {
1293
+ background: #38bdf8;
1294
+ color: #082a47;
1295
+ border: 1px solid #4fd3ff;
1296
+ box-shadow: 0 0 0 1px rgba(255,255,255,0.06), 0 6px 18px -4px rgba(0,0,0,0.55);
1297
+ padding: 10px 28px;
1298
+ width: auto; /* shrink to content */
1299
+ margin-top: 4px;
1300
+ letter-spacing: 0.6px;
1301
+ transition: background .25s, box-shadow .25s, transform .25s;
1302
+ }
1303
+ .side-panel.side-left .action-btn:hover {
1304
+ background: #4fd3ff;
1305
+ box-shadow: 0 0 0 1px rgba(255,255,255,0.08), 0 8px 22px -6px rgba(0,0,0,0.6);
1306
+ transform: translateY(-2px);
1307
+ }
1308
+ .side-panel.side-left .action-btn:active { transform: translateY(0); }
1309
+ .side-panel.side-left .action-btn:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }
1310
+ /* Responsive adjustment so card stays readable on narrow screens */
1311
+ @media (max-width: 900px) {
1312
+ .side-panel.side-left .side-welcome-overlay {
1313
+ left: 50%;
1314
+ top: auto;
1315
+ bottom: 20px;
1316
+ transform: translateX(-50%);
1317
+ max-width: 92%;
1318
+ padding: 18px 20px 22px;
1319
+ }
1320
+ }
1321
+ /* === End readability enhancements === */
1322
+
1323
+
1324
+ /* === Readability enhancements for right image overlay (scoped) === */
1325
+ .side-panel.side-right { position: relative; overflow:hidden; }
1326
+ .side-panel.side-right::before {
1327
+ content: "";
1328
+ position: absolute;
1329
+ inset: 0;
1330
+ background: linear-gradient(205deg, rgba(5,25,46,0.85) 10%, rgba(5,25,46,0.70) 45%, rgba(5,25,46,0.32) 78%);
1331
+ z-index: 1; /* above image, below info card */
1332
+ pointer-events: none;
1333
+ }
1334
+ .side-panel.side-right .side-info-box {
1335
+ position: absolute;
1336
+ top: 38%;
1337
+ left: 7%;
1338
+ z-index: 2;
1339
+ background: rgba(15, 40, 70, 0.55);
1340
+ backdrop-filter: blur(6px) saturate(140%);
1341
+ -webkit-backdrop-filter: blur(6px) saturate(140%);
1342
+ padding: 20px 24px 24px;
1343
+ border-radius: 18px;
1344
+ border: 1px solid rgba(255, 255, 255, 0.15);
1345
+ box-shadow: 0 4px 20px -4px rgba(0, 0, 0, 0.55), 0 0 0 1px rgba(255, 255, 255, 0.05);
1346
+ max-width: 469px;
1347
+ /* width: auto; */
1348
+ text-align: center;
1349
+ }
1350
+ .side-panel.side-right .welcome-back-title,
1351
+ .side-panel.side-right .welcome-back-desc { text-shadow: 0 2px 6px rgba(0,0,0,0.6); }
1352
+ .side-panel.side-right .action-btn {
1353
+ background: #38bdf8;
1354
+ color: #082a47;
1355
+ border: 1px solid #4fd3ff;
1356
+ box-shadow: 0 0 0 1px rgba(255,255,255,0.06), 0 6px 18px -4px rgba(0,0,0,0.55);
1357
+ padding: 10px 28px;
1358
+ width: auto;
1359
+ margin-top: 4px;
1360
+ letter-spacing: 0.6px;
1361
+ transition: background .25s, box-shadow .25s, transform .25s;
1362
+ }
1363
+ .side-panel.side-right .action-btn:hover {
1364
+ background: #4fd3ff;
1365
+ box-shadow: 0 0 0 1px rgba(255,255,255,0.08), 0 8px 22px -6px rgba(0,0,0,0.6);
1366
+ transform: translateY(-2px);
1367
+ }
1368
+ .side-panel.side-right .action-btn:active { transform: translateY(0); }
1369
+ .side-panel.side-right .action-btn:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }
1370
+ @media (max-width: 900px) {
1371
+ .side-panel.side-right .side-info-box {
1372
+ left: 50%;
1373
+ top: auto;
1374
+ bottom: 20px;
1375
+ transform: translateX(-50%);
1376
+ max-width: 92%;
1377
+ padding: 18px 20px 22px;
1378
+ }
1379
+ }
1380
+ .side-panel.side-right {
1381
+ position: relative;
1382
+ overflow: hidden;
1383
+ background: #1d608b; /* Strong blue for contrast */
1384
+ min-height: 400px;
1385
+ }
1386
+ .side-img {
1387
+ position: absolute;
1388
+ top:0; left:0;
1389
+ width:100%; height:100%;
1390
+ object-fit: cover;
1391
+ z-index:0;
1392
+ }
1393
+ .side-bg-shapes {
1394
+ position: absolute;
1395
+ top:0; left:0; right:0; bottom:0;
1396
+ width:100%; height:100%;
1397
+ z-index:1;
1398
+ pointer-events: none;
1399
+ }
1400
+ .bg-circle {
1401
+ position: absolute;
1402
+ border-radius:50%;
1403
+ opacity:0.85;
1404
+ }
1405
+ .circle1 {
1406
+ width: 120px;
1407
+ height: 120px;
1408
+ top: 18px;
1409
+ left: 18px;
1410
+ background: linear-gradient(135deg, #38bdf8 80%, #137ec4 100%);
1411
+ }
1412
+ .circle2 {
1413
+ width: 80px;
1414
+ height: 80px;
1415
+ top: 71%;
1416
+ left: 113px;
1417
+ background: linear-gradient(135deg, #38bdf8 80%, #137ec4 100%);
1418
+ }
1419
+
1420
+ .circle3 {
1421
+ width: 48px;
1422
+ height: 48px;
1423
+ top: 80%;
1424
+ left: 70%;
1425
+ background: linear-gradient(135deg, #38bdf8 80%, #137ec4 100%);
1426
+ }
1427
+
1428
+ .circle4 {
1429
+ width: 160px;
1430
+ height: 160px;
1431
+ top: 70px;
1432
+ right: 204px;
1433
+ background: linear-gradient(135deg, #38bdf8 80%, #137ec4 100%);
1434
+ opacity: 0.7;
1435
+ }
1436
+
1437
+ .circle5 {
1438
+ width: 36px;
1439
+ height: 36px;
1440
+ top: 30%;
1441
+ left: 80%;
1442
+ background: linear-gradient(135deg, #38bdf8 80%, #137ec4 100%);
1443
+ opacity: 0.6;
1444
+ }
1445
+
1446
+ .circle6 {
1447
+ width: 60px;
1448
+ height: 60px;
1449
+ bottom: 12%;
1450
+ right: 18%;
1451
+ background: linear-gradient(135deg, #38bdf8 80%, #137ec4 100%);
1452
+ opacity: 0.5;
1453
+ }
1454
+
1455
+ .circle7 {
1456
+ width: 54px;
1457
+ height: 54px;
1458
+ top: 10%;
1459
+ right: 10%;
1460
+ background: #18314a;
1461
+ opacity: 0.8;
1462
+ }
1463
+
1464
+ .circle8 {
1465
+ width: 32px;
1466
+ height: 32px;
1467
+ bottom: 20%;
1468
+ left: 60%;
1469
+ background: #14263c;
1470
+ opacity: 0.8;
1471
+ }
1472
+
1473
+ .circle9 {
1474
+ width: 44px;
1475
+ height: 44px;
1476
+ top: 75%;
1477
+ left: 40%;
1478
+ background: #18314a;
1479
+ opacity: 0.7;
1480
+ }
1481
+
1482
+ .circle10 {
1483
+ width: 28px;
1484
+ height: 28px;
1485
+ top: 15%;
1486
+ left: 60%;
1487
+ background: #14263c;
1488
+ opacity: 0.9;
1489
+ }
1490
+
1491
+ .circle11 {
1492
+ width: 38px;
1493
+ height: 38px;
1494
+ bottom: 10%;
1495
+ right: 10%;
1496
+ background: #18314a;
1497
+ opacity: 0.7;
1498
+ }
1499
+
1500
+ .circle12 {
1501
+ width: 22px;
1502
+ height: 22px;
1503
+ top: 85%;
1504
+ right: 20%;
1505
+ background: #14263c;
1506
+ opacity: 0.8;
1507
+ }
1508
+
1509
+ /* New large solid circles for sign-in page background */
1510
+ .circle-large1 {
1511
+ width:220px;
1512
+ height:220px;
1513
+ top:40px;
1514
+ left:60px;
1515
+ background: #137ec4;
1516
+ opacity:0.45;
1517
+ z-index:0;
1518
+ }
1519
+ .circle-large2 {
1520
+ width:180px;
1521
+ height:180px;
1522
+ bottom:40px;
1523
+ right:40px;
1524
+ background: #38bdf8;
1525
+ opacity:0.35;
1526
+ z-index:0;
1527
+ }
1528
+ .circle-large3 {
1529
+ width:140px;
1530
+ height:140px;
1531
+ top:60%;
1532
+ left:60%;
1533
+ background: #18314a;
1534
+ opacity:0.25;
1535
+ z-index:0;
1536
+ }
1537
+ .circle-large4 {
1538
+ width:100px;
1539
+ height:100px;
1540
+ bottom:11%;
1541
+ left:5%;
1542
+ background: #13bfa6;
1543
+ opacity:0.52;
1544
+ z-index:0;
1545
+ }
1546
+
1547
+ /* Add more white rings for contrast */
1548
+ .bg-ring {
1549
+ position: absolute;
1550
+ border-radius:50%;
1551
+ border:6px solid #fff;
1552
+ background: none;
1553
+ opacity:0.8;
1554
+ z-index:1;
1555
+ }
1556
+ .ring1 { width:90px; height:90px; bottom:24px; left:18px; }
1557
+ .ring2 { width:48px; height:48px; top:60px; right:32px; }
1558
+ .ring3 { width:72px; height:72px; top:10%; left:70%; border-color: #fff; opacity:0.7; }
1559
+ .ring4 { width:110px; height:110px; bottom:2%; right:43%; border-color: #fff; opacity:0.5; }
1560
+ .ring5 { width:38px; height:38px; top:75%; left:10%; border-color: #fff; opacity:0.8; }
1561
+ .ring6 { width:64px; height:64px; top:49%; right:20%; border-color: #fff; opacity:0.7; }
1562
+ .ring7 { width:120px; height:120px; top:14%; left:49%; border-color: #fff; opacity:0.6; }
1563
+ .ring8 { width:80px; height:80px; bottom:20%; right:30%; border-color: #fff; opacity:0.7; }
1564
+ .ring9 { width:60px; height:60px; top:65%; left:36%; border-color: #fff; opacity:0.7; }
1565
+
1566
+ .side-welcome-overlay {
1567
+ position: absolute;
1568
+ top:30%;
1569
+ left:50px;
1570
+ width:93%;
1571
+ text-align: start;
1572
+ z-index:2;
1573
+ padding:024px;
1574
+ pointer-events: auto;
1575
+ }
1576
+ .welcome-back-title {
1577
+ font-size:2.1rem;
1578
+ font-weight:800;
1579
+ color: #fff;
1580
+ margin-bottom:8px;
1581
+
1582
+ }
1583
+ .welcome-back-desc {
1584
+ font-size:1rem;
1585
+ color: #e0f7fa;
1586
+ margin-bottom:18px;
1587
+
1588
+ }
1589
+ .action-btn {
1590
+ width:21%;
1591
+ background: #18314a;
1592
+ color: #fff;
1593
+ border: none;
1594
+ border-radius:8px;
1595
+ padding:14px 0;
1596
+ font-size:1.1rem;
1597
+ font-weight:700;
1598
+ margin-top:18px;
1599
+ margin-bottom:0;
1600
+ display: inline-block;
1601
+ letter-spacing:0.5px;
1602
+ box-shadow:02px 8px #0003;
1603
+ cursor: pointer;
1604
+ transition: background 0.2s, color 0.2s;
1605
+ }
1606
+ .action-btn:hover {
1607
+ background: #38bdf8;
1608
+ color: #18314a;
1609
+ }
1610
+
1611
+ /* Place reveal icon inline when inside the password label */
1612
+ .password-label {
1613
+ display: inline-flex;
1614
+ align-items: center;
1615
+ gap: 8px;
1616
+ }
1617
+
1618
+ /* Wrap for input with trailing icon */
1619
+ .input-with-icon {
1620
+ position: relative;
1621
+ display: flex;
1622
+ align-items: center;
1623
+ }
1624
+ .input-with-icon input {
1625
+ width: 100%;
1626
+ padding-right: 42px; /* space for the icon */
1627
+ }
1628
+ .input-with-icon .eye-toggle {
1629
+ position: absolute;
1630
+ right: 12px;
1631
+ top: 50%;
1632
+ transform: translateY(-50%);
1633
+ background: none;
1634
+ border: none;
1635
+ font-size: 1.2em;
1636
+ color: #888;
1637
+ cursor: pointer;
1638
+ line-height: 1;
1639
+ padding: 0;
1640
+ opacity: 0.8;
1641
+ }
1642
+ .input-with-icon .eye-toggle:hover {
1643
+ opacity: 1;
1644
+ color: #555;
1645
+ }
1646
+
1647
+ /* add invalid input red outline */
1648
+ .signin-field input.invalid {
1649
+ border: 1px solid #ff5252;
1650
+ }
1651
+
1652
+ /* === Readability enhancements for left image overlay (scoped) === */
1653
+ .side-panel.side-left { position: relative; }
1654
+ .side-panel.side-left::before {
1655
+ content: "";
1656
+ position: absolute;
1657
+ inset: 0;
1658
+ background: linear-gradient(205deg, rgba(5,25,46,0.85) 8%, rgba(5,25,46,0.70) 40%, rgba(5,25,46,0.35) 75%);
1659
+ z-index: 1; /* sits above the image, below text card */
1660
+ pointer-events: none;
1661
+ }
1662
+ /* Frosted glass card for the welcome text */
1663
+ .side-panel.side-left .side-welcome-overlay {
1664
+ position: absolute;
1665
+ top: 44%;
1666
+ left: 10%;
1667
+ z-index: 2;
1668
+ background: rgba(15, 40, 70, 0.55);
1669
+ backdrop-filter: blur(6px) saturate(140%);
1670
+ -webkit-backdrop-filter: blur(6px) saturate(140%);
1671
+ padding: 20px 24px 24px;
1672
+ border-radius: 18px;
1673
+ border: 1px solid rgba(255, 255, 255, 0.15);
1674
+ box-shadow: 0 4px 20px -4px rgba(0, 0, 0, 0.55), 0 0 0 1px rgba(255, 255, 255, 0.05);
1675
+ max-width: 429px;
1676
+ width: auto;
1677
+
1678
+ text-align: center;
1679
+ }
1680
+ /* Strengthen text contrast */
1681
+ .side-panel.side-left .welcome-back-desc,
1682
+ .side-panel.side-left .welcome-back-title { text-shadow: 0 2px 6px rgba(0,0,0,0.6); }
1683
+ /* Refine button inside overlay only */
1684
+ .side-panel.side-left .action-btn {
1685
+ background: #38bdf8;
1686
+ color: #082a47;
1687
+ border: 1px solid #4fd3ff;
1688
+ box-shadow: 0 0 0 1px rgba(255,255,255,0.06), 0 6px 18px -4px rgba(0,0,0,0.55);
1689
+ padding: 10px 28px;
1690
+ width: auto; /* shrink to content */
1691
+ margin-top: 4px;
1692
+ letter-spacing: 0.6px;
1693
+ transition: background .25s, box-shadow .25s, transform .25s;
1694
+ }
1695
+ .side-panel.side-left .action-btn:hover {
1696
+ background: #4fd3ff;
1697
+ box-shadow: 0 0 0 1px rgba(255,255,255,0.08), 0 8px 22px -6px rgba(0,0,0,0.6);
1698
+ transform: translateY(-2px);
1699
+ }
1700
+ .side-panel.side-left .action-btn:active { transform: translateY(0); }
1701
+ .side-panel.side-left .action-btn:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }
1702
+ /* Responsive adjustment so card stays readable on narrow screens */
1703
+ @media (max-width: 900px) {
1704
+ .side-panel.side-left .side-welcome-overlay {
1705
+ left: 50%;
1706
+ top: auto;
1707
+ bottom: 20px;
1708
+ transform: translateX(-50%);
1709
+ max-width: 92%;
1710
+ padding: 18px 20px 22px;
1711
+ }
1712
+ }
1713
+ /* === End readability enhancements === */
1714
+
1715
+
1716
+ /* === Readability enhancements for right image overlay (scoped) === */
1717
+ .side-panel.side-right { position: relative; overflow:hidden; }
1718
+ .side-panel.side-right::before {
1719
+ content: "";
1720
+ position: absolute;
1721
+ inset: 0;
1722
+ background: linear-gradient(205deg, rgba(5,25,46,0.85) 10%, rgba(5,25,46,0.70) 45%, rgba(5,25,46,0.32) 78%);
1723
+ z-index: 1; /* above image, below info card */
1724
+ pointer-events: none;
1725
+ }
1726
+ .side-panel.side-right .side-info-box {
1727
+ position: absolute;
1728
+ top: 38%;
1729
+ left: 7%;
1730
+ z-index: 2;
1731
+ background: rgba(15, 40, 70, 0.55);
1732
+ backdrop-filter: blur(6px) saturate(140%);
1733
+ -webkit-backdrop-filter: blur(6px) saturate(140%);
1734
+ padding: 20px 24px 24px;
1735
+ border-radius: 18px;
1736
+ border: 1px solid rgba(255, 255, 255, 0.15);
1737
+ box-shadow: 0 4px 20px -4px rgba(0, 0, 0, 0.55), 0 0 0 1px rgba(255, 255, 255, 0.05);
1738
+ max-width: 469px;
1739
+ /* width: auto; */
1740
+ text-align: center;
1741
+ }
1742
+ .side-panel.side-right .welcome-back-title,
1743
+ .side-panel.side-right .welcome-back-desc { text-shadow: 0 2px 6px rgba(0,0,0,0.6); }
1744
+ .side-panel.side-right .action-btn {
1745
+ background: #38bdf8;
1746
+ color: #082a47;
1747
+ border: 1px solid #4fd3ff;
1748
+ box-shadow: 0 0 0 1px rgba(255,255,255,0.06), 0 6px 18px -4px rgba(0,0,0,0.55);
1749
+ padding: 10px 28px;
1750
+ width: auto;
1751
+ margin-top: 4px;
1752
+ letter-spacing: 0.6px;
1753
+ transition: background .25s, box-shadow .25s, transform .25s;
1754
+ }
1755
+ .side-panel.side-right .action-btn:hover {
1756
+ background: #4fd3ff;
1757
+ box-shadow: 0 0 0 1px rgba(255,255,255,0.08), 0 8px 22px -6px rgba(0,0,0,0.6);
1758
+ transform: translateY(-2px);
1759
+ }
1760
+ .side-panel.side-right .action-btn:active { transform: translateY(0); }
1761
+ .side-panel.side-right .action-btn:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }
1762
+ @media (max-width: 900px) {
1763
+ .side-panel.side-right .side-info-box {
1764
+ left: 50%;
1765
+ top: auto;
1766
+ bottom: 20px;
1767
+ transform: translateX(-50%);
1768
+ max-width: 92%;
1769
+ padding: 18px 20px 22px;
1770
+ }
1771
+ }
1772
+
1773
+ /* Top-left brand logo inside popup */
1774
+ .signin-brand-logo {
1775
+ position: absolute;
1776
+ top: 18px;
1777
+ left: 24px;
1778
+ z-index: 1200;
1779
+
1780
+ }
1781
+ .signin-brand-logo .brand-link { display:flex; align-items:center; gap:10px; text-decoration:none; }
1782
+ .brand-logo-img { /* width: 42px; */
1783
+ height: 42px;
1784
+ display: block;
1785
+ filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.4));
1786
+ max-width: 5vw;
1787
+ height: auto;
1788
+ background: #fff;
1789
+ border-radius: 1vw;
1790
+ margin: 0.5vw;
1791
+ }
1792
+ .brand-text { font-size:1.05rem; font-weight:700; letter-spacing:0.6px; color:#fff; text-shadow:0 2px 6px rgba(0,0,0,0.6); font-family:inherit; }
1793
+ .signin-brand-logo:hover .brand-text { color:#4fd3ff; }
1794
+
1795
+ @media (max-width:700px){
1796
+ .signin-brand-logo { top:10px; left:10px; padding:6px 12px 6px 8px; }
1797
+ .brand-logo-img { width:34px; height:34px; }
1798
+ .brand-text { font-size:0.9rem; }
1799
+ }
1800
+
1801
+ /* Bottom-right watermark */
1802
+ .signin-watermark {
1803
+ text-decoration: none;
1804
+ position: absolute;
1805
+ bottom: 14px;
1806
+ right: 22px;
1807
+ font-size: 1vw;
1808
+ font-weight: 600;
1809
+ letter-spacing: 0.5px;
1810
+ color: #ffffff;
1811
+
1812
+ text-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
1813
+ }
1814
+ @media (max-width:700px){
1815
+ .signin-watermark { bottom:8px; right:12px; font-size:0.65rem; }
1816
+ }
1817
+
1818
+ /* Social Media Icons Top-Right for Sign-In (Home Style) */
1819
+ .social-icons.signin-social-icons {
1820
+ position: absolute;
1821
+ top: 18px;
1822
+ right: 24px;
1823
+ z-index: 1200;
1824
+ display: flex;
1825
+ gap: 18px;
1826
+ }
1827
+ .social-icons.signin-social-icons .social-icon {
1828
+ width: 42px;
1829
+ height: 42px;
1830
+ display: inline-flex;
1831
+ align-items: center;
1832
+ justify-content: center;
1833
+ border-radius: 50%;
1834
+ background-color: #fff;
1835
+ color: #38bdf8;
1836
+ font-size: 18px;
1837
+ box-shadow: 0 0 0 1px #214055, 0 4px 14px #0006;
1838
+ transition: background-color 0.25s, color 0.25s, transform 0.25s, box-shadow 0.25s;
1839
+ text-decoration: none;
1840
+ }
1841
+ .social-icons.signin-social-icons .social-icon.facebook {
1842
+ color: #1877f2;
1843
+ }
1844
+ .social-icons.signin-social-icons .social-icon.youtube {
1845
+ color: #ff0000;
1846
+ }
1847
+ .social-icons.signin-social-icons .social-icon.linkedin {
1848
+ color: #0a66c2;
1849
+ }
1850
+ .social-icons.signin-social-icons .social-icon.instagram {
1851
+ color: #fd5949;
1852
+ }
1853
+ .social-icons.signin-social-icons .social-icon.facebook:hover {
1854
+ background-color: #1877f2;
1855
+ color: #fff;
1856
+ }
1857
+ .social-icons.signin-social-icons .social-icon.youtube:hover {
1858
+ background-color: #ff0000;
1859
+ color: #fff;
1860
+ }
1861
+ .social-icons.signin-social-icons .social-icon.linkedin:hover {
1862
+ background-color: #0a66c2;
1863
+ color: #fff;
1864
+ }
1865
+ .social-icons.signin-social-icons .social-icon.instagram:hover {
1866
+ background: radial-gradient(circle at 30% 110%, #fdf497 0%, #fd5949 45%, #d6249f 60%, #285aeb 90%);
1867
+ color: #fff;
1868
+ filter: brightness(1.15);
1869
+ box-shadow: 0 6px 22px rgba(253, 89, 73, .6);
1870
+ }
1871
+ .social-icons.signin-social-icons .social-icon.website:hover {
1872
+ background-color: #009688;
1873
+ color: #fff;
1874
+ transform: translateY(-4px);
1875
+ box-shadow: 0 6px 20px #00968888, 0 0 0 2px #009688 inset;
1876
+ }
1877
+ @media (max-width:700px){
1878
+ .social-icons.signin-social-icons {
1879
+ top: 10px;
1880
+ right: 10px;
1881
+ gap: 10px;
1882
+ }
1883
+ .social-icons.signin-social-icons .social-icon {
1884
+ width: 28px;
1885
+ height: 28px;
1886
+ font-size: 1em;
1887
+ }
1888
+ }
1889
+
src/app/sign-in/sign-in.component.html ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <section class="signin-popup ai-bg-animate">
2
+ <div class="ai-particle-bg"></div>
3
+ <!-- Added brand logo top-left -->
4
+ <div class="signin-brand-logo" role="banner">
5
+ <a routerLink="/" aria-label="Pykara Home" class="brand-link">
6
+ <img [src]="brand.logo" alt="Pykara Logo" class="brand-logo-img" />
7
+
8
+
9
+ </a>
10
+ </div>
11
+
12
+ <!-- Social Media Icons Top-Right -->
13
+ <div class="social-icons signin-social-icons">
14
+ <a href="https://www.linkedin.com/in/pykara-technologies" target="_blank" class="social-icon linkedin" aria-label="LinkedIn">
15
+ <i class="fab fa-linkedin-in"></i>
16
+ </a>
17
+ <a href="https://www.youtube.com/@PykaraTechnologies" target="_blank" class="social-icon youtube" aria-label="YouTube">
18
+ <i class="fab fa-youtube"></i>
19
+ </a>
20
+ <a href="https://www.facebook.com/people/Pykara/100087653675803/" target="_blank" class="social-icon facebook" aria-label="Facebook">
21
+ <i class="fab fa-facebook-f"></i>
22
+ </a>
23
+ <a href="https://www.instagram.com/pykaratechnologie/" target="_blank" class="social-icon instagram" aria-label="Instagram">
24
+ <i class="fab fa-instagram"></i>
25
+ </a>
26
+ <a href="https://pykara.ai/" target="_blank" class="social-icon website" aria-label="Website">
27
+ <i class="fas fa-globe"></i>
28
+ </a>
29
+ </div>
30
+
31
+ <div class="signin-header">
32
+ </div>
33
+
34
+ <div class="auth-card" [class.flipped]="isFlipped">
35
+ <div class="card-inner">
36
+ <!-- FRONT: sign-in on main panel, image side on left -->
37
+ <div class="card-front">
38
+ <div class="card-content">
39
+ <div class="side-panel side-left">
40
+ <!-- Decorative background shapes -->
41
+ <img class="side-img" src="assets/images/login/lion.png" alt="Lion" />
42
+ <!-- Overlay text and button -->
43
+ <div class="side-welcome-overlay">
44
+
45
+ <div class="welcome-back-desc">First time here? Create your account to get started.</div>
46
+ <button class="action-btn" type="button" (click)="flipToSignUp()">Sign Up</button>
47
+ </div>
48
+ <!-- Replace /assets/side-placeholder-left.jpg with your chosen image -->
49
+ </div>
50
+
51
+ <div class="main-panel">
52
+ <!-- brand header above login -->
53
+ <div class="logo-header">
54
+ <span class="py-learn-text">{{ brand.name }}</span>
55
+ <div class="self-learning-system">(A Self-Learning System)</div>
56
+ </div>
57
+ <!-- existing sign-in form -->
58
+ <button class="signin-close" type="button" (click)="closePopup()" aria-label="Close">&times;</button>
59
+ <h2 class="signin-title">
60
+
61
+ <span class="login-text">Login</span>
62
+ <span [innerHTML]="typingTitle"></span>
63
+ </h2>
64
+
65
+ <form [formGroup]="form" (ngSubmit)="signIn()" novalidate>
66
+ <div class="signin-row">
67
+ <div class="signin-field">
68
+ <label for="email">Email or Username</label>
69
+ <input id="email" type="email" placeholder="you@example.com" formControlName="email" [class.invalid]="submitted && form.get('email')?.invalid" [attr.aria-invalid]="form.get('email')?.invalid && form.get('email')?.touched" />
70
+ <small *ngIf="submitted && form.get('email')?.errors?.['required']" class="error">Email or Username is required.</small>
71
+ </div>
72
+ </div>
73
+ <div class="signin-row">
74
+ <div class="signin-field">
75
+ <label for="password">Password</label>
76
+ <div class="input-with-icon">
77
+ <input id="password" [type]="showPassword ? 'text' : 'password'" placeholder="••••••••" formControlName="password" [class.invalid]="submitted && form.get('password')?.invalid" [attr.aria-invalid]="form.get('password')?.invalid && form.get('password')?.touched" />
78
+ <button type="button"
79
+ class="eye-toggle"
80
+ (click)="togglePasswordVisibility()"
81
+ tabindex="-1"
82
+ [attr.aria-pressed]="showPassword"
83
+ aria-label="Show/Hide password">
84
+ {{ showPassword ? '🙈' : '👁️' }}
85
+ </button>
86
+ </div>
87
+ <small *ngIf="submitted && form.get('password')?.errors?.['required']" class="error">Password is required.</small>
88
+ <div *ngIf="errorMessage" class="signin-error-toast">{{ errorMessage }}</div>
89
+ </div>
90
+ </div>
91
+ <div class="signin-row signin-options-row">
92
+ <div class="remember-me">
93
+ <label class="switch">
94
+ <input id="rememberMe" type="checkbox" />
95
+ <span class="slider"></span>
96
+ </label>
97
+ <span>Remember me</span>
98
+ </div>
99
+ <div class="forgot-password">
100
+ <a href="#" class="forgot-link" (click)="openForgotModal($event)">Forgot password?</a>
101
+ </div>
102
+ </div>
103
+ <!--<div class="signin-hint">Press Enter to Sign In</div>-->
104
+ <button class="signin-btn ai-pulse" type="submit" [disabled]="loading">
105
+ <ng-container *ngIf="!loading; else loggingIn">
106
+ Login
107
+ </ng-container>
108
+ <ng-template #loggingIn>
109
+ <span class="spinner"></span> Logging in...
110
+ </ng-template>
111
+ </button>
112
+
113
+ <!-- Divider with 'or' -->
114
+ <div class="signin-divider-row">
115
+ <div class="divider"></div>
116
+ <span class="divider-or">or</span>
117
+ <div class="divider"></div>
118
+ </div>
119
+
120
+ <!-- Custom Google Sign-In button -->
121
+ <button class="google-btn" type="button" (click)="onGoogleSignIn()">
122
+ <img src="assets/images/google-logo.svg" alt="Google logo" class="google-logo" />
123
+ <span>Log in with Google</span>
124
+ </button>
125
+
126
+ </form>
127
+ </div>
128
+ </div>
129
+ </div>
130
+
131
+ <!-- BACK: sign-up on main panel, image side on right -->
132
+ <div class="card-back">
133
+ <div class="card-content card-content-reverse">
134
+ <div class="main-panel">
135
+ <div class="panel-right-embed">
136
+
137
+ <app-sign-up [embedded]="true" (switchToSignIn)="flipToSignIn()"></app-sign-up>
138
+ </div>
139
+ <div class="signin-footer">
140
+ <a href="#" (click)="flipToSignIn(); $event.preventDefault()">Back to Sign In</a>
141
+ </div>
142
+ </div>
143
+
144
+ <!-- Right side: lion image + requested minimal text/buttons -->
145
+ <div class="side-panel side-right">
146
+ <div class="side-info-box">
147
+ <div class="welcome-back-title">Learn Smarter.</div>
148
+ <div class="welcome-back-title">Practice Better.</div>
149
+ <div class="welcome-back-desc">Existing user? Log in to your account.</div>
150
+ <button class="action-btn" type="button" (click)="goToLogin()">Sign In</button>
151
+ </div>
152
+ <img class="side-img" src="assets/images/login/lion.png" alt="Lion" />
153
+ </div>
154
+ </div>
155
+ </div>
156
+
157
+ </div>
158
+ </div>
159
+
160
+ <div *ngIf="showForgotModal" class="forgot-modal-bg">
161
+ <div class="forgot-modal">
162
+ <h3>Forgot Password</h3>
163
+ <p>Enter your email to receive password reset instructions.</p>
164
+ <input type="email" [(ngModel)]="forgotEmail" placeholder="Your email" />
165
+ <button class="signin-btn" (click)="sendForgotEmail()">Send Reset Link</button>
166
+ <button class="modal-close" (click)="closeForgotModal()">Close</button>
167
+ </div>
168
+ </div>
169
+ <!-- Added bottom-right watermark -->
170
+ <!--<div class="signin-watermark" aria-hidden="true">© {{ brand.name }} 2025</div>-->
171
+ <a class="signin-watermark" href="https://pykara.ai" target="_blank">www.pykara.ai</a>
172
+ </section>
src/app/sign-in/sign-in.component.spec.ts ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+
3
+ import { SignInComponent } from './sign-in.component';
4
+
5
+ describe('SignInComponent', () => {
6
+ let component: SignInComponent;
7
+ let fixture: ComponentFixture<SignInComponent>;
8
+
9
+ beforeEach(() => {
10
+ TestBed.configureTestingModule({
11
+ declarations: [SignInComponent]
12
+ });
13
+ fixture = TestBed.createComponent(SignInComponent);
14
+ component = fixture.componentInstance;
15
+ fixture.detectChanges();
16
+ });
17
+
18
+ it('should create', () => {
19
+ expect(component).toBeTruthy();
20
+ });
21
+ });
src/app/sign-in/sign-in.component.ts ADDED
@@ -0,0 +1,291 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // sign-in.component.ts
2
+ import { Component, ChangeDetectionStrategy, Output, EventEmitter, ViewChild, ElementRef, HostListener, ChangeDetectorRef, AfterViewInit, OnDestroy } from '@angular/core';
3
+ import { CommonModule } from '@angular/common';
4
+ import { ReactiveFormsModule, FormBuilder, Validators, FormGroup, NgModel } from '@angular/forms';
5
+ import { Router, RouterLink } from '@angular/router';
6
+ import { FormsModule } from '@angular/forms';
7
+ import { SignUpComponent } from '../sign-up/sign-up.component';
8
+ import { trigger, transition, style, animate } from '@angular/animations';
9
+ import { AuthService } from '../auth/auth.service';
10
+ import { BrandService } from '../shared/brand.service';
11
+
12
+ declare const google: any;
13
+
14
+ @Component({
15
+ selector: 'app-sign-in',
16
+ standalone: true,
17
+ imports: [CommonModule, ReactiveFormsModule, FormsModule, RouterLink, SignUpComponent],
18
+ templateUrl: './sign-in.component.html',
19
+ styleUrls: ['./sign-in.component.css'],
20
+ changeDetection: ChangeDetectionStrategy.OnPush,
21
+ animations: [
22
+ trigger('fadeInOut', [
23
+ transition(':enter', [
24
+ style({ opacity: 0 }),
25
+ animate('600ms', style({ opacity: 1 }))
26
+ ]),
27
+ transition(':leave', [
28
+ animate('600ms', style({ opacity: 0 }))
29
+ ])
30
+ ])
31
+ ]
32
+ })
33
+ export class SignInComponent implements AfterViewInit, OnDestroy {
34
+ @Output() switchToSignUp = new EventEmitter<void>();
35
+ @Output() close = new EventEmitter<void>();
36
+ form: FormGroup;
37
+ loading = false;
38
+ errorMessage = '';
39
+ showForgotModal = false;
40
+ forgotEmail = '';
41
+ typingTitle = '';
42
+ private fullTitle = 'Log In';
43
+ private typingIndex = 0;
44
+ private typingInterval: any;
45
+ showPassword = false;
46
+ googleButtonRendered = false;
47
+ submitted = false;
48
+
49
+ // Slide state (false = show front/sign-in, true = show back/sign-up)
50
+ isFlipped = false;
51
+
52
+ @ViewChild('cardRoot', { static: true }) cardRoot!: ElementRef<HTMLElement>;
53
+
54
+ facts: string[] = [
55
+ '🎯 Practice grammar with adaptive quizzes.',
56
+ '📖 Improve reading with AI-generated passages.',
57
+ '🗣️ Train listening and pronunciation effectively.'
58
+ ];
59
+ currentFact: string = this.facts[0];
60
+ private factIndex = 0;
61
+ private factInterval: any;
62
+
63
+ constructor(
64
+ private fb: FormBuilder,
65
+ private router: Router,
66
+ private authService: AuthService,
67
+ public brand: BrandService,
68
+ private cdr: ChangeDetectorRef // Added for change detection
69
+ ) {
70
+ this.form = this.fb.group({
71
+ // accept username or email -> only require a non-empty value
72
+ email: ['', [Validators.required]],
73
+ password: ['', [Validators.required]]
74
+ });
75
+ }
76
+
77
+ ngAfterViewInit() {
78
+ // ensure initial aria states
79
+ this.updateAriaStates();
80
+ // load Google Identity Services button
81
+ this.loadGoogleScript();
82
+ this.startFactRotation();
83
+ }
84
+
85
+ ngOnDestroy() {
86
+ if (this.factInterval) {
87
+ clearInterval(this.factInterval);
88
+ }
89
+ }
90
+
91
+ startTypingAnimation() {
92
+ this.typingTitle = '';
93
+ this.typingIndex = 0;
94
+ this.fullTitle = 'Verifying Identity …';
95
+ this.typingInterval = setInterval(() => {
96
+ if (this.typingIndex < this.fullTitle.length) {
97
+ this.typingTitle += this.fullTitle[this.typingIndex++];
98
+ } else {
99
+ clearInterval(this.typingInterval);
100
+ setTimeout(() => {
101
+ this.typingTitle = 'Log In';
102
+ }, 1200);
103
+ }
104
+ }, 60);
105
+ }
106
+
107
+ loadGoogleScript() {
108
+ if (!document.getElementById('google-identity')) {
109
+ const script = document.createElement('script');
110
+ script.src = 'https://accounts.google.com/gsi/client';
111
+ script.async = true;
112
+ script.id = 'google-identity';
113
+ script.onload = () => {
114
+ this.renderGoogleButton();
115
+ };
116
+ document.body.appendChild(script);
117
+ } else {
118
+ this.renderGoogleButton();
119
+ }
120
+ }
121
+
122
+ renderGoogleButton() {
123
+ if (this.googleButtonRendered) return;
124
+ const clientId = 'YOUR_GOOGLE_CLIENT_ID.apps.googleusercontent.com'; // Replace with your client ID
125
+ const googleBtnDiv = document.getElementById('google-btn-div');
126
+ if (googleBtnDiv && typeof google !== 'undefined' && google.accounts && google.accounts.id) {
127
+ google.accounts.id.initialize({
128
+ client_id: clientId,
129
+ callback: (response: any) => this.handleGoogleCredential(response)
130
+ });
131
+ google.accounts.id.renderButton(googleBtnDiv, {
132
+ theme: 'outline', // or 'filled_blue' for blue
133
+ size: 'large',
134
+ text: 'continue_with',
135
+ shape: 'pill',
136
+ width: '100%'
137
+ });
138
+ this.googleButtonRendered = true;
139
+ }
140
+ }
141
+
142
+ signIn() {
143
+ this.submitted = true;
144
+ this.form.markAllAsTouched();
145
+
146
+ if (this.form.invalid) {
147
+ // focus first invalid control for accessibility
148
+ setTimeout(() => {
149
+ const firstInvalid = document.querySelector('.main-panel [formControlName].ng-invalid') as HTMLElement | null;
150
+ if (firstInvalid) firstInvalid.focus();
151
+ });
152
+ this.errorMessage = '';
153
+ this.cdr.markForCheck();
154
+ return;
155
+ }
156
+
157
+ this.loading = true;
158
+ this.errorMessage = '';
159
+
160
+ const username = this.form.get('email')?.value; // can be username or email
161
+ const password = this.form.get('password')?.value;
162
+
163
+ this.authService.login(username, password).subscribe(
164
+ (response) => {
165
+ this.loading = false;
166
+ this.authService.setLoggedIn(true);
167
+ this.authService.startAutoRefresh();
168
+ localStorage.setItem('username', username);
169
+
170
+ const redirectUrl = localStorage.getItem('redirectAfterLogin') || '/home';
171
+ localStorage.removeItem('redirectAfterLogin');
172
+ this.router.navigate([redirectUrl]);
173
+ },
174
+ (error) => {
175
+ this.loading = false;
176
+ console.log('Sign-in error:', error);
177
+ this.errorMessage = 'Invalid username or password';
178
+ this.cdr.markForCheck();
179
+ setTimeout(() => {
180
+ this.errorMessage = '';
181
+ this.cdr.markForCheck();
182
+ }, 3000);
183
+ }
184
+ );
185
+ }
186
+
187
+ handleGoogleCredential(response: any) {
188
+ if (response.credential) {
189
+ // TODO: Send credential to backend and handle login
190
+ this.router.navigate(['/home']);
191
+ } else {
192
+ this.errorMessage = 'Google sign-in failed.';
193
+ setTimeout(() => { this.errorMessage = ''; }, 3000);
194
+ }
195
+ }
196
+
197
+ openForgotModal(event: Event) {
198
+ event.preventDefault();
199
+ this.showForgotModal = true;
200
+ }
201
+ closeForgotModal() {
202
+ this.showForgotModal = false;
203
+ this.forgotEmail = '';
204
+ }
205
+ sendForgotEmail() {
206
+ // Simulate sending email
207
+ this.closeForgotModal();
208
+ alert('Password reset link sent to your email.');
209
+ }
210
+
211
+ @HostListener('document:keydown.enter', ['$event'])
212
+ handleEnterKey(event: KeyboardEvent) {
213
+ if (!this.showForgotModal && !this.loading) {
214
+ this.signIn();
215
+ }
216
+ }
217
+
218
+ // Slide controls
219
+ flipToSignUp() {
220
+ this.isFlipped = true;
221
+ this.updateAriaStates();
222
+ setTimeout(() => {
223
+ const el = document.querySelector('.card-back .main-panel input');
224
+ if (el) (el as HTMLElement).focus();
225
+ }, 700);
226
+ }
227
+ flipToSignIn() {
228
+ this.isFlipped = false;
229
+ this.updateAriaStates();
230
+ setTimeout(() => {
231
+ const el = document.querySelector('.card-front .main-panel input');
232
+ if (el) (el as HTMLElement).focus();
233
+ }, 700);
234
+ }
235
+
236
+ updateAriaStates() {
237
+ const front = document.querySelector('.card-front');
238
+ const back = document.querySelector('.card-back');
239
+ if (!front || !back) return;
240
+ if (this.isFlipped) {
241
+ front.setAttribute('aria-hidden', 'true');
242
+ back.setAttribute('aria-hidden', 'false');
243
+ } else {
244
+ front.setAttribute('aria-hidden', 'false');
245
+ back.setAttribute('aria-hidden', 'true');
246
+ }
247
+ }
248
+
249
+ goToSignUp() {
250
+ this.flipToSignUp();
251
+ }
252
+ goToSignUpPage() {
253
+ this.router.navigate(['/sign-up']);
254
+ }
255
+
256
+ closePopup() {
257
+ // Navigate to home when closing the popup
258
+ this.router.navigate(['/home']);
259
+
260
+ this.close.emit();
261
+ try {
262
+ const modal = document.querySelector('.modal');
263
+ if (modal && modal.parentElement) modal.parentElement.removeChild(modal);
264
+ const backdrop = document.querySelector('.modal-backdrop');
265
+ if (backdrop && backdrop.parentElement) backdrop.parentElement.removeChild(backdrop);
266
+ } catch (e) {
267
+ console.warn('Failed to remove modal/backdrop DOM elements', e);
268
+ }
269
+ this.cdr.markForCheck();
270
+ }
271
+
272
+ togglePasswordVisibility() {
273
+ this.showPassword = !this.showPassword;
274
+ }
275
+
276
+ goToLogin() {
277
+ this.flipToSignIn();
278
+ }
279
+
280
+ onGoogleSignIn() {
281
+ console.log('Google sign-in clicked');
282
+ }
283
+
284
+ startFactRotation() {
285
+ this.factInterval = setInterval(() => {
286
+ this.factIndex = (this.factIndex + 1) % this.facts.length;
287
+ this.currentFact = this.facts[this.factIndex];
288
+ this.cdr.markForCheck();
289
+ }, 5000);
290
+ }
291
+ }
src/app/sign-in/sign-in.service.spec.ts ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { TestBed } from '@angular/core/testing';
2
+
3
+ import { SignInService } from './sign-in.service';
4
+
5
+ describe('SignInService', () => {
6
+ let service: SignInService;
7
+
8
+ beforeEach(() => {
9
+ TestBed.configureTestingModule({});
10
+ service = TestBed.inject(SignInService);
11
+ });
12
+
13
+ it('should be created', () => {
14
+ expect(service).toBeTruthy();
15
+ });
16
+ });
src/app/sign-in/sign-in.service.ts ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Injectable } from '@angular/core';
2
+ import { HttpClient } from '@angular/common/http';
3
+ import { Observable, throwError } from 'rxjs';
4
+ import { catchError } from 'rxjs/operators';
5
+
6
+ @Injectable({
7
+ providedIn: 'root'
8
+ })
9
+ export class SignInService {
10
+
11
+ constructor(private http: HttpClient) { }
12
+
13
+ signIn(payload: any): Observable<any> {
14
+ return this.http.post('http://127.0.0.1:5000/sign-in', payload).pipe(
15
+ catchError((error) => {
16
+ return throwError(() => error);
17
+ })
18
+ );
19
+ }
20
+ }
src/app/sign-up/sign-up.component.css ADDED
@@ -0,0 +1,671 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ :host {
2
+ display: block;
3
+ width: 100%;
4
+ min-height: 100vh;
5
+ }
6
+
7
+ .signup-bg {
8
+ min-height: 100vh;
9
+ display: flex;
10
+ align-items: center;
11
+ justify-content: center;
12
+ background: #f7fafd;
13
+ }
14
+
15
+ .signup-container {
16
+ display: flex;
17
+ width: 100vw;
18
+ max-width: 1200px;
19
+ min-height: 600px;
20
+ border-radius: 0;
21
+ box-shadow: none;
22
+ overflow: hidden;
23
+ align-items: center;
24
+ justify-content: center;
25
+ }
26
+
27
+ .signup-panel-right {
28
+ display: flex;
29
+ align-items: center;
30
+ justify-content: center;
31
+ padding: 32px 0;
32
+ }
33
+
34
+ .signup-panel-right {
35
+ flex: 1 1 0;
36
+ background: #f7fafd;
37
+ }
38
+
39
+ .create-card {
40
+ background: #fff;
41
+ width: 90%;
42
+ max-width: 540px;
43
+ display: flex;
44
+ flex-direction: column;
45
+ align-items: center;
46
+ padding: 38px 38px 28px 38px;
47
+ border-radius: 18px;
48
+ box-shadow: 0 12px 48px rgba(2, 6, 23, 0.18), 0 0 0 2px rgba(56, 189, 248, 0.08);
49
+ margin: 0 auto;
50
+ }
51
+
52
+ .create-title {
53
+ font-size: 2.1rem;
54
+ font-weight: 900;
55
+ text-align: center;
56
+ margin-bottom: 28px;
57
+ letter-spacing: 0.6px;
58
+ color: #23395d;
59
+ }
60
+
61
+ .signup-title.center-title {
62
+ text-align: center;
63
+ margin-bottom: 32px;
64
+ width: 100%;
65
+ font-size: 2.1rem;
66
+ font-weight: 800;
67
+ letter-spacing: 1px;
68
+ color: #23395d;
69
+ text-shadow: 0 2px 8px #0008;
70
+ animation: logoGlow 3.5s ease-in-out infinite alternate;
71
+ }
72
+
73
+ @keyframes logoGlow {
74
+ 0% {
75
+ text-shadow: 0 2px 8px #0008, 0 0 12px #38bdf8, 0 0 6px #13bfa6;
76
+ }
77
+
78
+ 100% {
79
+ text-shadow: 0 2px 8px #0008, 0 0 32px #38bdf8, 0 0 18px #13bfa6;
80
+ }
81
+ }
82
+
83
+ .create-form {
84
+ width: 100%;
85
+ max-width: 510px;
86
+ display: grid;
87
+ grid-template-columns: 1fr 1fr;
88
+ gap: 12px 15px;
89
+ align-items: start;
90
+ margin-bottom: 14px;
91
+ }
92
+
93
+ .terms-info {
94
+ color: #137ec4;
95
+ font-size: 1.08rem;
96
+ font-weight: 600;
97
+ text-align: left;
98
+ margin: 12px 0 0 0;
99
+ letter-spacing: 0.5px;
100
+ display: block;
101
+ }
102
+
103
+ .form-row {
104
+ display: contents;
105
+ }
106
+
107
+ .form-field {
108
+ display: flex;
109
+ flex-direction: column;
110
+ gap: 8px;
111
+ width: 100%;
112
+ }
113
+
114
+ .form-field label {
115
+ font-size: 1.05rem;
116
+ font-weight: 700;
117
+ color: #23395d;
118
+ }
119
+
120
+ .form-field input,
121
+ .form-field select {
122
+ background: #fff;
123
+ color: #23395d;
124
+ border: none;
125
+ border-radius: 8px;
126
+ padding: 12px 14px;
127
+ font-size: 1rem;
128
+ margin-bottom: 2px;
129
+ box-shadow: 0 1px 4px #0002;
130
+ transition: border 0.2s, box-shadow 0.2s;
131
+ width: 100%;
132
+ min-width: 0;
133
+ max-width: 100%;
134
+ height: 46px;
135
+ box-sizing: border-box;
136
+ }
137
+
138
+ .form-field input:focus,
139
+ .form-field select:focus {
140
+ outline: 2px solid #1de9b6;
141
+ border-color: #1de9b6;
142
+ box-shadow: 0 0 6px rgba(56, 189, 248, 0.5), 0 0 0 2px #1de9b688;
143
+ }
144
+
145
+ .form-field input::placeholder {
146
+ color: #b0b8c1;
147
+ opacity: 1;
148
+ }
149
+
150
+ .form-checkbox {
151
+ grid-column: 1 / -1;
152
+ display: flex;
153
+ gap: 10px;
154
+ align-items: center;
155
+ color: #2b5160;
156
+ margin-top: 8px;
157
+ }
158
+
159
+ .form-checkbox input[type="checkbox"] {
160
+ width: 18px;
161
+ height: 18px;
162
+ accent-color: #38bdf8;
163
+ }
164
+
165
+ .create-btn {
166
+ grid-column: 1 / -1;
167
+ width: 100%;
168
+ background: #23395d;
169
+ color: #fff;
170
+ padding: 14px 18px;
171
+ border-radius: 10px;
172
+ font-weight: 800;
173
+ border: none;
174
+ box-shadow: 0 10px 30px rgba(3, 20, 36, 0.32);
175
+ cursor: pointer;
176
+ font-size: 1.15rem;
177
+ margin-top: 10px;
178
+ }
179
+
180
+ .create-btn:hover {
181
+ background: #38bdf8;
182
+ color: #fff;
183
+ }
184
+
185
+ .create-login-link {
186
+ grid-column: 1 / -1;
187
+ text-align: center;
188
+ color: #137ec4;
189
+ margin-top: 0px;
190
+ }
191
+
192
+ .create-login-link a {
193
+ color: #137ec4;
194
+ font-weight: 700;
195
+ }
196
+
197
+ .create-footer {
198
+ grid-column: 1 / -1;
199
+ text-align: center;
200
+ color: #010207;
201
+ font-size: 0.9rem;
202
+ margin-top: 4vw;
203
+ }
204
+
205
+ .form-field .error {
206
+ color: #ff5252;
207
+ font-size: 0.85rem;
208
+ margin-top: 0px;
209
+ }
210
+
211
+ .welcome-info-box {
212
+ position: absolute;
213
+ top: 32px;
214
+ left: 0;
215
+ width: 100%;
216
+ padding: 0 32px;
217
+ z-index: 2;
218
+ text-align: left;
219
+ }
220
+
221
+ .welcome-info-title {
222
+ font-size: 1.35rem;
223
+ font-weight: 800;
224
+ color: #fff;
225
+ margin-bottom: 6px;
226
+ letter-spacing: 0.5px;
227
+ }
228
+
229
+ .welcome-info-desc {
230
+ font-size: 1.08rem;
231
+ color: #e0f7fa;
232
+ margin-bottom: 8px;
233
+ }
234
+
235
+ .welcome-info-link {
236
+ color: #fff;
237
+ font-weight: 700;
238
+ text-decoration: underline;
239
+ cursor: pointer;
240
+ transition: color 0.2s;
241
+ }
242
+
243
+ .welcome-info-link:hover {
244
+ color: #23395d;
245
+ }
246
+
247
+ /* Extra whitespace and centering for small screens */
248
+ @media (max-width: 900px) {
249
+ .signup-container {
250
+ flex-direction: column;
251
+ width: 98vw;
252
+ align-items: center;
253
+ justify-content: center;
254
+ }
255
+
256
+ .signup-panel-right {
257
+ padding: 18px 6vw;
258
+ }
259
+
260
+ .create-card {
261
+ padding: 18px 6vw;
262
+ margin: 0 auto;
263
+ }
264
+
265
+ .create-form {
266
+ grid-template-columns: 1fr;
267
+ gap: 18px;
268
+ }
269
+
270
+ .create-btn {
271
+ width: 100%;
272
+ }
273
+ }
274
+
275
+ @media (max-width: 600px) {
276
+ .create-card {
277
+ padding: 10px 2vw;
278
+ }
279
+
280
+ .signup-panel-right {
281
+ padding: 10px 2vw;
282
+ }
283
+ }
284
+
285
+ .signin-close {
286
+ position: absolute;
287
+ top: 5px;
288
+ right: 5px;
289
+ width: 38px;
290
+ height: 38px;
291
+ border: none;
292
+ background: #14263c;
293
+ color: #fff;
294
+ border-radius: 50%;
295
+ font-size: 2rem;
296
+ font-weight: bold;
297
+ display: flex;
298
+ align-items: center;
299
+ justify-content: center;
300
+ cursor: pointer;
301
+ z-index: 10;
302
+ transition: background 0.2s, color 0.2s;
303
+ box-shadow: 0 2px 8px #0005;
304
+ }
305
+
306
+ .signin-close:hover {
307
+ background: #38bdf8;
308
+ color: #18314a;
309
+ }
310
+
311
+ /* Eye toggle inside password fields: match sign-in positioning */
312
+ .form-field .eye-toggle {
313
+ position: absolute;
314
+ right: 12px;
315
+ top: 38px;
316
+ background: none;
317
+ border: none;
318
+ font-size: 1.3em;
319
+ color: #888;
320
+ cursor: pointer;
321
+ z-index: 2;
322
+ padding: 0;
323
+ line-height: 1;
324
+ opacity: 0.9;
325
+ transition: color 0.2s, opacity 0.2s;
326
+ }
327
+
328
+ .form-field .eye-toggle:hover {
329
+ color: #555;
330
+ opacity: 1;
331
+ }
332
+
333
+ .form-field .eye-toggle:focus {
334
+ outline: none;
335
+ }
336
+
337
+ /* Ensure button SVG scales nicely */
338
+ .form-field .eye-toggle svg {
339
+ width: 22px;
340
+ height: 22px;
341
+ }
342
+
343
+ /* Small screen tweak: move eye toggle slightly up if spacing differs */
344
+ @media (max-width: 700px) {
345
+ .form-field .eye-toggle {
346
+ top: 28px;
347
+ }
348
+ }
349
+
350
+ .fact-rotator {
351
+ font-size: 1.18rem;
352
+ font-weight: 700;
353
+ color: #fff;
354
+ margin-bottom: 18px;
355
+ min-height: 32px;
356
+ text-align: center;
357
+ transition: opacity 0.6s;
358
+ letter-spacing: 0.5px;
359
+ animation: fadeFact 0.6s;
360
+ }
361
+
362
+ @keyframes fadeFact {
363
+ from {
364
+ opacity: 0;
365
+ }
366
+
367
+ to {
368
+ opacity: 1;
369
+ }
370
+ }
371
+
372
+ .side-panel.side-right {
373
+ position: relative;
374
+ overflow: hidden;
375
+ display: flex;
376
+ align-items: center;
377
+ justify-content: center;
378
+ background: linear-gradient(135deg, #38bdf8 0%, #7b2ff2 100%);
379
+ min-height: 100%;
380
+ }
381
+
382
+ .side-panel.side-left {
383
+ position: relative;
384
+ overflow: hidden;
385
+ display: flex;
386
+ align-items: center;
387
+ justify-content: center;
388
+
389
+ min-height: 100%;
390
+ }
391
+
392
+ .wave-bg {
393
+ position: absolute;
394
+ inset: 0;
395
+ width: 100%;
396
+ height: 100%;
397
+ z-index: 1;
398
+ overflow: hidden;
399
+ }
400
+
401
+ .wave-svg {
402
+ position: absolute;
403
+ top: 0;
404
+ left: 0;
405
+ width: 100%;
406
+ height: 100%;
407
+ z-index: 1;
408
+ }
409
+
410
+ .circle {
411
+ position: absolute;
412
+ border-radius: 50%;
413
+ background: linear-gradient(135deg, #38bdf8 0%, #7b2ff2 100%);
414
+ opacity: 0.85;
415
+ }
416
+
417
+ .circle1 {
418
+ width: 90px;
419
+ height: 90px;
420
+ top: 80px;
421
+ left: 60px;
422
+ box-shadow: 0 0 32px #7b2ff2aa;
423
+ }
424
+
425
+ .circle2 {
426
+ width: 60px;
427
+ height: 60px;
428
+ top: 220px;
429
+ left: 220px;
430
+ box-shadow: 0 0 24px #38bdf8aa;
431
+ }
432
+
433
+ .circle3 {
434
+ width: 120px;
435
+ height: 120px;
436
+ bottom: 40px;
437
+ left: 120px;
438
+ box-shadow: 0 0 48px #7b2ff2aa;
439
+ }
440
+
441
+ .circle4 {
442
+ width: 36px;
443
+ height: 36px;
444
+ bottom: 80px;
445
+ right: 60px;
446
+ box-shadow: 0 0 12px #38bdf8aa;
447
+ }
448
+
449
+ .welcome-content {
450
+ position: relative;
451
+ z-index: 2;
452
+ width: 100%;
453
+ text-align: center;
454
+ display: flex;
455
+ flex-direction: column;
456
+ align-items: center;
457
+ justify-content: center;
458
+ margin-top: 80px;
459
+ }
460
+
461
+ .welcome-title {
462
+ font-size: 2.2rem;
463
+ font-weight: 800;
464
+ color: #fff;
465
+ margin-bottom: 18px;
466
+ text-shadow: 0 2px 16px #0006;
467
+ }
468
+
469
+ .welcome-subtitle {
470
+ font-size: 1.08rem;
471
+ color: #e0e7ef;
472
+ margin-bottom: 32px;
473
+ letter-spacing: 1px;
474
+ text-shadow: 0 2px 8px #0004;
475
+ }
476
+
477
+ .welcome-footer {
478
+ font-size: 1.05rem;
479
+ color: #fff;
480
+ opacity: 0.7;
481
+ margin-top: 120px;
482
+ letter-spacing: 2px;
483
+ }
484
+
485
+ /* Glossy info popup for role info */
486
+ .role-info-popup-bg {
487
+ position: fixed;
488
+ inset: 0;
489
+ background: rgba(30, 41, 59, 0.55);
490
+ backdrop-filter: blur(1px);
491
+ z-index: 0;
492
+ display: flex;
493
+ align-items: center;
494
+ justify-content: flex-end;
495
+ padding: 48px 56px 48px 24px;
496
+ animation: fadeInModalBg 0.3s;
497
+ }
498
+
499
+ @keyframes fadeInModalBg {
500
+ from {
501
+ opacity: 0;
502
+ }
503
+
504
+ to {
505
+ opacity: 1;
506
+ }
507
+ }
508
+
509
+ @keyframes popupOpen {
510
+ 0% {
511
+ opacity: 0;
512
+ transform: scale(0.92) translateY(24px);
513
+ }
514
+
515
+ 100% {
516
+ opacity: 1;
517
+ transform: scale(1) translateY(0);
518
+ }
519
+ }
520
+
521
+ .role-info-popup {
522
+ background: rgba(255, 255, 255, 0.92);
523
+ border-radius: 4px;
524
+ box-shadow: 0 8px 32px #38bdf844 0 24px #1e293b88;
525
+ padding: 22px 28px 18px 28px;
526
+ min-width: 220px;
527
+ max-width: 90vw;
528
+ text-align: left;
529
+ z-index: 3001;
530
+ font-size: 0.98em;
531
+ color: #23395d;
532
+ letter-spacing: 0.2px;
533
+ line-height: 1.5;
534
+ position: relative;
535
+ font-family: inherit;
536
+ opacity: 1;
537
+ animation: popupOpen 1.2s cubic-bezier(.22, .9, .32, 1) both;
538
+ }
539
+
540
+ .role-info-popup .close-btn {
541
+ position: absolute;
542
+ top: 8px;
543
+ right: 8px;
544
+ width: 24px;
545
+ height: 24px;
546
+ border: none;
547
+ background: #14263c;
548
+ color: #fff;
549
+ border-radius: 50%;
550
+ font-size: 1.1rem;
551
+ font-weight: bold;
552
+ display: flex;
553
+ align-items: center;
554
+ justify-content: center;
555
+ cursor: pointer;
556
+ z-index: 10;
557
+ transition: background 0.2s, color 0.2s;
558
+ box-shadow: 0 2px 8px #0005;
559
+ }
560
+
561
+ .role-info-popup .close-btn:hover {
562
+ background: #38bdf8;
563
+ color: #18314a;
564
+ }
565
+
566
+ .role-info-btn {
567
+ background: none;
568
+ border: none;
569
+ color: #38bdf8;
570
+ font-size: 1.15em;
571
+ cursor: pointer;
572
+ margin-left: 6px;
573
+ vertical-align: middle;
574
+ padding: 0;
575
+ display: inline-flex;
576
+ align-items: center;
577
+ justify-content: center;
578
+ transition: color 0.2s;
579
+ }
580
+
581
+ .role-info-btn:hover {
582
+ color: #137ec4;
583
+ }
584
+
585
+ /* Spinner for loading state on buttons */
586
+ .spinner {
587
+ display: inline-block;
588
+ width:18px;
589
+ height:18px;
590
+ border:3px solid #fff;
591
+ border-top:3px solid #38bdf8;
592
+ border-radius:50%;
593
+ animation: spin0.7s linear infinite;
594
+ vertical-align: middle;
595
+ margin-right:8px;
596
+ }
597
+ @keyframes spin {
598
+ 0% { transform: rotate(0deg);}
599
+ 100% { transform: rotate(360deg);}
600
+ }
601
+
602
+ /* Info button and floating info popup styles */
603
+ .info-btn {
604
+ background: #38bdf8;
605
+ color: #fff;
606
+ border: none;
607
+ border-radius:50%;
608
+ width:28px;
609
+ height:28px;
610
+ font-size:1.1rem;
611
+ font-weight: bold;
612
+ cursor: pointer;
613
+ margin-left:8px;
614
+ }
615
+
616
+ .info-popup-bg {
617
+ position: fixed;
618
+ inset:0;
619
+ background: rgba(30,41,59,0.45);
620
+ backdrop-filter: blur(2px);
621
+ z-index:;
622
+ display: flex;
623
+ align-items: center;
624
+ justify-content: center;
625
+ }
626
+
627
+ .info-popup {
628
+ background: rgba(255,255,255,0.85);
629
+ border-radius: 16px;
630
+ box-shadow: 08px 32px #38bdf844,0024px #1e293b88;
631
+ padding: 24px 28px 18px 28px;
632
+ min-width: 320px;
633
+ max-width: 90vw;
634
+ text-align: left;
635
+ font-size: 0.98rem;
636
+ color: #23395d;
637
+ position: relative;
638
+ font-family: inherit;
639
+ left: 840px;
640
+ }
641
+
642
+ .info-title {
643
+ font-size:1.08rem;
644
+ font-weight:700;
645
+ margin-bottom:8px;
646
+ color: #38bdf8;
647
+ letter-spacing:0.5px;
648
+ }
649
+
650
+ .info-text {
651
+ font-size:0.95rem;
652
+ color: #23395d;
653
+ opacity:0.95;
654
+ }
655
+
656
+ .info-close {
657
+ position: absolute;
658
+ top:8px;
659
+ right:12px;
660
+ background: none;
661
+ border: none;
662
+ font-size:1.5rem;
663
+ color: #38bdf8;
664
+ cursor: pointer;
665
+ font-weight: bold;
666
+ opacity:0.7;
667
+ transition: opacity 0.2s;
668
+ }
669
+ .info-close:hover {
670
+ opacity:1;
671
+ }
src/app/sign-up/sign-up.component.html ADDED
@@ -0,0 +1,155 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <section class="signup-popup ai-bg-animate">
2
+ <div class="ai-particle-bg"></div>
3
+ <div class="signup-header">
4
+ <div class="signup-logo">
5
+
6
+ </div>
7
+ </div>
8
+
9
+ <div class="auth-card">
10
+ <div class="card-inner">
11
+ <!-- FRONT: sign-up on main panel, image side on left -->
12
+ <div class="card-front">
13
+ <button class="signin-close" type="button" (click)="closePopup()" aria-label="Close">&times;</button>
14
+
15
+ <div class="card-content">
16
+ <div class="side-panel side-left">
17
+ <div class="signup-panel-left">
18
+
19
+ </div>
20
+ <div class="main-panel">
21
+ <h2 class="signup-title center-title">Create An Account</h2>
22
+
23
+ <form [formGroup]="form" (ngSubmit)="submit()" class="create-form" novalidate>
24
+ <div class="form-row">
25
+ <div class="form-field">
26
+ <label for="firstName">First Name</label>
27
+ <input id="firstName" type="text" placeholder="First Name" formControlName="name" [attr.aria-invalid]="controlHasError('name')" />
28
+ <small *ngIf="controlHasError('name','required') && form.get('name')?.touched" class="error">First name is required.</small>
29
+ <small *ngIf="controlHasError('name','minlength') && form.get('name')?.touched" class="error">Enter at least 2 characters.</small>
30
+ <small *ngIf="controlHasError('name','invalidName') && form.get('name')?.touched" class="error">Only alphabets and spaces allowed.</small>
31
+ </div>
32
+ <div class="form-field">
33
+ <label for="lastName">Last Name</label>
34
+ <input id="lastName" type="text" placeholder="Last Name" formControlName="lastName" [attr.aria-invalid]="controlHasError('lastName')" />
35
+ <small *ngIf="controlHasError('lastName','required') && form.get('lastName')?.touched" class="error">Last name is required.</small>
36
+ <small *ngIf="controlHasError('lastName','minlength') && form.get('lastName')?.touched" class="error">Enter at least 2 characters.</small>
37
+ <small *ngIf="controlHasError('lastName','invalidName') && form.get('lastName')?.touched" class="error">Only alphabets and spaces allowed.</small>
38
+ </div>
39
+ </div>
40
+ <div class="form-row">
41
+ <div class="form-field">
42
+ <label for="email">Email</label>
43
+ <input id="email" type="text" placeholder="email@gmail.com" formControlName="email" [attr.aria-invalid]="controlHasError('email')" />
44
+ <small *ngIf="controlHasError('email','required') && form.get('email')?.touched" class="error">Email is required.</small>
45
+ <small *ngIf="controlHasError('email','pattern') && form.get('email')?.touched" class="error">Enter a valid email/phone.</small>
46
+ </div>
47
+ <div class="form-field role-field-wrapper">
48
+ <label for="role">
49
+ Role
50
+ <button class="info-btn" type="button" (click)="showInfo = true">i</button>
51
+ </label>
52
+ <select id="role" formControlName="role" [attr.aria-invalid]="controlHasError('role')">
53
+ <option value="">-- Select Role --</option>
54
+ <option value="admin">Admin</option>
55
+ <option value="teachers">Teachers</option>
56
+ <option value="student">Student</option>
57
+ <!-- <option value="investigators">Investigators</option>-->
58
+ <option value="others">Others</option>
59
+ </select>
60
+ <small *ngIf="controlHasError('role','required') && form.get('role')?.touched" class="error">Role is required.</small>
61
+ </div>
62
+ </div>
63
+ <div class="form-row">
64
+ <div class="form-field" style="position:relative;">
65
+ <label for="password">Create Password</label>
66
+ <input id="password" [type]="showPassword ? 'text' : 'password'" placeholder="••••••••" formControlName="password" [attr.aria-invalid]="controlHasError('password')" />
67
+ <button type="button" class="eye-toggle" (click)="toggleConfirmPasswordVisibility()" tabindex="-1" aria-label="Show/Hide confirm password">
68
+ </button>
69
+ <small *ngIf="controlHasError('password','required') && form.get('password')?.touched" class="error">Password is required.</small>
70
+ <small *ngIf="controlHasError('password','minlength') && form.get('password')?.touched" class="error">Use at least 8 characters.</small>
71
+ <small *ngIf="form.get('password')?.hasError('passwordPolicy') && form.get('password')?.touched" class="policy-info">
72
+ Create a strong password with at least 8 characters using letters, numbers, and special symbols.
73
+ </small>
74
+ </div>
75
+
76
+ <div class="form-field" style="position:relative;">
77
+ <label for="confirmPassword">Confirm Password</label>
78
+ <input id="confirmPassword" [type]="showConfirmPassword ? 'text' : 'password'" placeholder="••••••••" formControlName="confirmPassword" [attr.aria-invalid]="showPwdMismatch()" />
79
+ <button type="button" class="eye-toggle" (click)="toggleConfirmPasswordVisibility()" tabindex="-1" aria-label="Show/Hide confirm password">
80
+ </button>
81
+ <small *ngIf="controlHasError('confirmPassword','required') && form.get('confirmPassword')?.touched" class="error">Confirm password is required.</small>
82
+ <small *ngIf="showPwdMismatch() && form.get('confirmPassword')?.touched" class="error">Passwords do not match.</small>
83
+ </div>
84
+ </div>
85
+ <div class="form-checkbox">
86
+ <input type="checkbox" id="terms" formControlName="terms" />
87
+ <label for="terms">Creating your account and you accepting <a href="#">Terms & Conditions.</a></label>
88
+ </div>
89
+ <div *ngIf="submitted && !form.get('terms')?.value" class="terms-info">
90
+ Please accept Terms &amp; Conditions.
91
+ </div>
92
+ <button class="create-btn ai-pulse" type="submit" [disabled]="loading">
93
+ <ng-container *ngIf="!loading; else creatingAccount">
94
+ Create Account
95
+ </ng-container>
96
+ <ng-template #creatingAccount>
97
+ <span class="spinner"></span> Creating Account...
98
+ </ng-template>
99
+ </button>
100
+
101
+
102
+ <!-- Google Sign-In button -->
103
+ <div class="google-signup-row">
104
+ <div id="google-signup-btn-div">
105
+ <div class="g-signin2" data-width="240" data-height="50" data-longtitle="true"></div>
106
+ </div>
107
+ </div>
108
+
109
+ <div class="create-footer"><b>© Pykara Technologies, 2025. All rights reserved.</b></div>
110
+ </form>
111
+ </div>
112
+ </div>
113
+ </div>
114
+ </div>
115
+ </div>
116
+ <!-- Detached overlay/modal so layout doesn't shift -->
117
+ <!--<div class="role-help-overlay" *ngIf="showRoleInfo" (click)="hideRoleInfo()">
118
+ <div class="role-help-modal" role="dialog" aria-label="Role descriptions" (click)="$event.stopPropagation()">
119
+ <button type="button" class="role-help-close" aria-label="Close role info" (click)="hideRoleInfo()">×</button>
120
+ <h3 class="role-help-title">Choose the right role</h3>
121
+ <ul class="role-help-list">
122
+ <li><strong>Admin:</strong> Full control: users, roles, system settings.</li>
123
+ <li><strong>Teachers:</strong> Run assessments / training evaluations.</li>
124
+ <li><strong>Student:</strong> Access learning modules, exercises.</li>-->
125
+
126
+ <!--<li><strong>Lawyers:</strong> Review analytics for case & witness prep.</li>
127
+ <li><strong>Investigators:</strong> Conduct interviews and capture signals.</li>-->
128
+ <!--<li><strong>Others:</strong> General or limited access usage.</li>
129
+ </ul>
130
+ <p class="role-help-tip">Not sure? Select Others; an Admin can upgrade your role later.</p>
131
+ </div>
132
+ </div>-->
133
+
134
+ <!-- Floating Info Popup -->
135
+ <div *ngIf="showInfo" class="info-popup-bg">
136
+ <div class="info-popup">
137
+ <button class="info-close" type="button" (click)="showInfo = false">&times;</button>
138
+ <div class="info-title">Role Information</div>
139
+ <div class="info-text">
140
+ <ul>
141
+ <li><strong>Admin:</strong> Full control: users, roles, system settings.</li>
142
+ <li><strong>Teachers:</strong> Run assessments / training evaluations.</li>
143
+ <li><strong>Student:</strong> Access learning modules, exercises.</li>
144
+
145
+ <li><strong>Others:</strong> General or limited access usage.</li>
146
+ </ul>
147
+ <p>Not sure? Select Others; an Admin can upgrade your role later.</p>
148
+ </div>
149
+ </div>
150
+ </div>
151
+
152
+
153
+
154
+
155
+
src/app/sign-up/sign-up.component.spec.ts ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+
3
+ import { SignUpComponent } from './sign-up.component';
4
+
5
+ describe('SignUpComponent', () => {
6
+ let component: SignUpComponent;
7
+ let fixture: ComponentFixture<SignUpComponent>;
8
+
9
+ beforeEach(() => {
10
+ TestBed.configureTestingModule({
11
+ declarations: [SignUpComponent]
12
+ });
13
+ fixture = TestBed.createComponent(SignUpComponent);
14
+ component = fixture.componentInstance;
15
+ fixture.detectChanges();
16
+ });
17
+
18
+ it('should create', () => {
19
+ expect(component).toBeTruthy();
20
+ });
21
+ });
src/app/sign-up/sign-up.component.ts ADDED
@@ -0,0 +1,270 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Component, Output, EventEmitter, ChangeDetectorRef, Input, OnInit, OnDestroy } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import { FormBuilder, FormGroup, ReactiveFormsModule, Validators, AbstractControl, ValidationErrors } from '@angular/forms';
4
+ import { Router, RouterLink } from '@angular/router';
5
+ import { SignUpService } from './sign-up.service'; // Import the SignUpService
6
+ import { trigger, transition, style, animate } from '@angular/animations';
7
+
8
+ export function nameValidator(control: AbstractControl): ValidationErrors | null {
9
+ const value = control.value || '';
10
+ // Only allow alphabets and spaces, min2 chars
11
+ if (!/^[A-Za-z ]{2,}$/.test(value)) {
12
+ return { invalidName: true };
13
+ }
14
+ return null;
15
+ }
16
+
17
+ @Component({
18
+ selector: 'app-sign-up',
19
+ standalone: true,
20
+ imports: [CommonModule, ReactiveFormsModule, RouterLink],
21
+ templateUrl: './sign-up.component.html',
22
+ styleUrls: ['./sign-up.component.css'],
23
+ animations: [
24
+ trigger('fadeInOut', [
25
+ transition(':enter', [
26
+ style({ opacity:0 }),
27
+ animate('600ms', style({ opacity:1 }))
28
+ ]),
29
+ transition(':leave', [
30
+ animate('600ms', style({ opacity:0 }))
31
+ ])
32
+ ])
33
+ ]
34
+ })
35
+ export class SignUpComponent implements OnInit, OnDestroy {
36
+ @Input() embedded = false; // when true, render only inner panel (for embedding in auth-card)
37
+ @Output() switchToSignIn = new EventEmitter<void>();
38
+ form: FormGroup;
39
+ private isSubmitting = false;
40
+
41
+ // Added: state & handlers for role info popover used in template
42
+ showRoleInfo = false;
43
+ toggleRoleInfo(ev?: Event) { ev?.stopPropagation(); this.showRoleInfo = !this.showRoleInfo; }
44
+ hideRoleInfo() { this.showRoleInfo = false; }
45
+
46
+ @Output() close = new EventEmitter<void>();
47
+
48
+ showPassword = false;
49
+ showConfirmPassword = false;
50
+ errorMessage = '';
51
+
52
+ isSignUpActive = true; // ← Added state for sign-up panel activation
53
+ public loading = false; // Used to disable the button during sign-up
54
+ submitted = false; // Track form submission status
55
+
56
+ // Added: terms & conditions error handling
57
+ termsError: string = '';
58
+
59
+ facts: string[] = [
60
+ '🎯 Master grammar with adaptive quizzes.',
61
+ '📖 Build comprehension with AI reading practice.',
62
+ '🗣️ Improve listening and pronunciation with feedback.'
63
+ ];
64
+ currentFact: string = this.facts[0];
65
+ private factIndex =0;
66
+ private factInterval: any;
67
+
68
+ showInfo = false;
69
+
70
+ constructor(
71
+ private fb: FormBuilder,
72
+ private router: Router,
73
+ private signUpService: SignUpService,
74
+ private cdr: ChangeDetectorRef
75
+ ) {
76
+ this.form = this.fb.group({
77
+ name: ['', [Validators.required, Validators.minLength(2), nameValidator]],
78
+ lastName: ['', [Validators.required, Validators.minLength(2), nameValidator]],
79
+ email: ['', [
80
+ Validators.required,
81
+ Validators.pattern(/(^[^@]+@[^@]+\.[^@]+$)|(^\+?\d[\d\-\s]{8,14}\d$)/)
82
+ ]],
83
+ password: ['', [Validators.required, Validators.minLength(8), passwordPolicyValidator]],
84
+ confirmPassword: ['', [Validators.required]],
85
+ role: ['', [Validators.required]],
86
+ terms: [false, Validators.requiredTrue] // Added terms control with requiredTrue validator
87
+ }, { validators: [this.passwordsMatchValidator] });
88
+
89
+ // Close popover when clicking anywhere in document (capture phase not needed here)
90
+ document.addEventListener('click', () => this.hideRoleInfo());
91
+ }
92
+
93
+ ngOnInit() {
94
+ this.startFactRotation();
95
+ }
96
+
97
+ ngOnDestroy() {
98
+ if (this.factInterval) {
99
+ clearInterval(this.factInterval);
100
+ }
101
+ }
102
+
103
+ startFactRotation() {
104
+ this.factInterval = setInterval(() => {
105
+ this.factIndex = (this.factIndex +1) % this.facts.length;
106
+ this.currentFact = this.facts[this.factIndex];
107
+ },5000);
108
+ }
109
+
110
+ control(path: string): AbstractControl | null { return this.form.get(path); }
111
+
112
+ controlHasError(path: string, error?: string): boolean {
113
+ const c = this.control(path);
114
+ if (!c) return false;
115
+ return error ? !!(c.touched && c.errors?.[error]) : !!(c.touched && c.invalid);
116
+ }
117
+
118
+ showPwdMismatch(): boolean {
119
+ const pw = this.control('password');
120
+ const cpw = this.control('confirmPassword');
121
+ const groupMismatch = this.form.errors?.['passwordMismatch'];
122
+ return !!(pw && cpw && (cpw.touched || pw.touched) && groupMismatch);
123
+ }
124
+
125
+ passwordsMatchValidator(group: AbstractControl) {
126
+ const pw = group.get('password')?.value;
127
+ const cpw = group.get('confirmPassword')?.value;
128
+ return pw && cpw && pw === cpw ? null : { passwordMismatch: true };
129
+ }
130
+
131
+ togglePasswordVisibility() {
132
+ this.showPassword = !this.showPassword;
133
+ }
134
+ toggleConfirmPasswordVisibility() {
135
+ this.showConfirmPassword = !this.showConfirmPassword;
136
+ }
137
+
138
+ submit() {
139
+ // Confirm button click
140
+ alert("Sign-Up button clicked!");
141
+ console.log("Sign-Up button clicked!");
142
+
143
+ this.submitted = true; // Track the form submission attempt
144
+
145
+ // Mark all form controls as touched to trigger validation
146
+ this.form.markAllAsTouched();
147
+
148
+ // Check if the form is invalid
149
+ if (this.form.invalid) {
150
+ console.log("Form is invalid:");
151
+
152
+ // Log individual control states for debugging
153
+ Object.keys(this.form.controls).forEach(controlName => {
154
+ const control = this.form.get(controlName);
155
+ if (control?.invalid) {
156
+ console.log(`${controlName} is invalid. Errors:`, control?.errors);
157
+ }
158
+ });
159
+ return;
160
+ }
161
+
162
+ // Check terms & conditions acceptance
163
+ if (!this.form.get('terms')?.value) {
164
+ this.termsError = 'Please accept Terms & Conditions.';
165
+ return;
166
+ }
167
+ this.termsError = '';
168
+
169
+ this.loading = true; // Set loading to true when starting submission
170
+ try {
171
+ // Prepare the payload to send to the backend
172
+ const payload = {
173
+ name: this.control('name')?.value,
174
+ lastName: this.control('lastName')?.value,
175
+ email: this.control('email')?.value,
176
+ password: this.control('password')?.value,
177
+ role: this.control('role')?.value
178
+ };
179
+
180
+ console.log("Form data before submission:", this.form.value);
181
+ console.log("Payload to send:", payload);
182
+
183
+ // Make the HTTP request
184
+ this.signUpService.signUp(payload).subscribe(
185
+ (response) => {
186
+ this.errorMessage = '';
187
+ console.log("Sign-up request sent successfully!");
188
+ this.loading = false; // Reset loading on success
189
+ // On success: if embedded inside SignIn card, flip back to sign-in; else go to /login
190
+ if (this.embedded) {
191
+ this.switchToSignIn.emit();
192
+ } else {
193
+ this.router.navigate(['/login']);
194
+ }
195
+ },
196
+ (error) => {
197
+ if (error && error.status === 400) {
198
+ this.errorMessage = 'This email or username is already registered.';
199
+ } else {
200
+ this.errorMessage = 'An error occurred. Please try again.';
201
+ }
202
+ this.loading = false; // Reset loading on error
203
+ this.cdr.markForCheck();
204
+ setTimeout(() => {
205
+ this.errorMessage = '';
206
+ this.cdr.markForCheck();
207
+ }, 3000);
208
+ }
209
+ );
210
+ } catch (error) {
211
+ console.error("Error occurred during sign-up:", error); // Log any errors from the API call
212
+ this.loading = false; // Reset loading on exception
213
+ }
214
+ }
215
+
216
+ navigateHome() { this.router.navigateByUrl('/'); }
217
+
218
+ goToLogin() {
219
+ this.switchToSignIn.emit(); // ← Emit event instead of router navigation
220
+ }
221
+
222
+ closePopup() {
223
+ try {
224
+ // dispatch a global event so parent or other listeners always can close modals
225
+ window.dispatchEvent(new CustomEvent('auth-close'));
226
+ } catch (e) {
227
+ // ignore
228
+ }
229
+
230
+ this.close.emit();
231
+ // Defensive: remove modal/backdrop if parent didn't hide them
232
+ try {
233
+ const modal = document.querySelector('.modal');
234
+ if (modal && modal.parentElement) modal.parentElement.removeChild(modal);
235
+ const backdrop = document.querySelector('.modal-backdrop');
236
+ if (backdrop && backdrop.parentElement) backdrop.parentElement.removeChild(backdrop);
237
+ } catch (e) {
238
+ console.warn('Failed to remove modal/backdrop DOM elements', e);
239
+ }
240
+ // Ensure change detection updates
241
+ this.cdr.markForCheck();
242
+
243
+ // Navigate to home after closing
244
+ this.router.navigate(['/home']);
245
+ }
246
+
247
+ tr(key: string): string {
248
+ const map: Record<string, string> = { title: 'Create your account', subtitle: 'Join to continue' };
249
+ return map[key] || '';
250
+ }
251
+
252
+ goToSignIn() {
253
+ // Emit to parent when embedded so the card can slide back
254
+ this.switchToSignIn.emit();
255
+ }
256
+
257
+ goToSignUp() {
258
+ // no-op when embedded
259
+ }
260
+ }
261
+
262
+ function passwordPolicyValidator(control: AbstractControl): ValidationErrors | null {
263
+ const value = control.value || '';
264
+ // Policy: min 8 chars, 1 uppercase, 1 lowercase, 1 number, 1 special char
265
+ const policy = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*()_+\-=[\]{};':"\\|,.<>\/?]).{8,}$/;
266
+ if (!policy.test(value)) {
267
+ return { passwordPolicy: true };
268
+ }
269
+ return null;
270
+ }
src/app/sign-up/sign-up.service.spec.ts ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { TestBed } from '@angular/core/testing';
2
+
3
+ import { SignUpService } from './sign-up.service';
4
+
5
+ describe('SignUpService', () => {
6
+ let service: SignUpService;
7
+
8
+ beforeEach(() => {
9
+ TestBed.configureTestingModule({});
10
+ service = TestBed.inject(SignUpService);
11
+ });
12
+
13
+ it('should be created', () => {
14
+ expect(service).toBeTruthy();
15
+ });
16
+ });
src/app/sign-up/sign-up.service.ts ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Injectable } from '@angular/core';
2
+ import { HttpClient } from '@angular/common/http'; // Import HttpClient for API calls
3
+ import { Observable } from 'rxjs'; // Import Observable for async handling
4
+
5
+ @Injectable({
6
+ providedIn: 'root'
7
+ })
8
+ export class SignUpService {
9
+ private apiUrl = 'http://127.0.0.1:5000'; // The base URL for your Flask backend
10
+ constructor(private http: HttpClient) { }
11
+
12
+ // Method to sign up a user
13
+ signUp(payload: any): Observable<any> {
14
+ return this.http.post(`${this.apiUrl}/sign-up`, payload);
15
+ }
16
+ }
src/app/voice/voice.component.html CHANGED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ <app-header [title]="'Voice'"></app-header>
2
+
3
+ <img src="assets/images/grammar-bg.png" alt="Chat Background" class="grammar-bg" />
4
+
5
+ <div class="voice-container" style="padding: 2rem; color: #fff;">
6
+ <!-- TODO: implement voice module UI -->
7
+ <p>Voice module coming soon.</p>
8
+ </div>
src/assets/images/google-logo.svg ADDED

Git LFS Details

  • SHA256: 92965efec0c4d979aa06400f5789b3b769ff51494a071df0cec68ee4d68da5cf
  • Pointer size: 128 Bytes
  • Size of remote file: 784 Bytes
src/styles.css CHANGED
@@ -1,10 +1,10 @@
1
- @tailwind base;
2
- @tailwind components;
3
- @tailwind utilities;
4
- /*@import '~@fortawesome/fontawesome-free/css/all.min.css';*/
5
-
6
-
7
 
 
8
  @font-face {
9
  font-family: 'Super Cartoon';
10
  src: url('assets/font/Super Cartoon.ttf') format('truetype');
@@ -13,15 +13,6 @@
13
  font-display: swap;
14
  }
15
 
16
-
17
- h1 {
18
- font-size: 3vw;
19
- color: white;
20
- font-family: Super Cartoon;
21
- }
22
-
23
-
24
-
25
  @font-face {
26
  font-family: 'Raleway';
27
  src: url('assets/font/Raleway.woff2') format('woff2'), url('assets/font/Raleway.ttf') format('truetype');
@@ -30,7 +21,6 @@ h1 {
30
  font-display: swap;
31
  }
32
 
33
-
34
  @font-face {
35
  font-family: 'Amonk_Outline';
36
  src: url('assets/font/Amonk_Outline.ttf') format('truetype');
@@ -47,51 +37,107 @@ h1 {
47
  font-display: swap;
48
  }
49
 
50
- body, html {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  margin: 0;
52
  padding: 0;
53
- box-sizing: border-box;
54
  height: 100%;
55
  display: flex;
56
  flex-direction: column;
57
- font-family: Raleway;
58
  background-color: rgb(35 34 32 / 43%);
59
- /*overflow: hidden;*/
60
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  }
62
 
63
 
64
 
65
- /*Py-Learn text near Logo css*/
66
 
67
- /*Py-learn text*/
68
- /* Keep logo and text in one row */
69
  .logo {
70
  display: flex;
71
  align-items: center;
72
  gap: 1vw;
73
  }
74
 
75
- /* Make the anchor wrap both image and name */
76
  .brand-link {
77
  display: flex;
78
  align-items: center;
79
- gap: 0.6vw; /* space between logo and text */
80
  text-decoration: none;
81
  }
82
 
83
- /* “PY-Learn” text next to the logo */
84
  .product-name {
85
  color: #fff;
86
- font-size: 2vw; /* slightly smaller than the 3vw title */
87
- /*font-weight: 700;*/
88
  letter-spacing: 0.5px;
89
- /*font-family: 'Super Cartoon', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;*/
90
  font-family: Amonk_Outline;
91
  line-height: 1;
92
  }
93
 
94
  .brand-link:hover .product-name {
95
- opacity: 0.9; /* subtle hover */
96
  }
97
-
 
1
+ /* =========================================================
2
+ Global Stylesheet (styles.css)
3
+ - Aligned formatting and added section comments
4
+ - No functional changes to selectors or declarations
5
+ ========================================================= */
 
6
 
7
+ /* -------------------- Custom Fonts -------------------- */
8
  @font-face {
9
  font-family: 'Super Cartoon';
10
  src: url('assets/font/Super Cartoon.ttf') format('truetype');
 
13
  font-display: swap;
14
  }
15
 
 
 
 
 
 
 
 
 
 
16
  @font-face {
17
  font-family: 'Raleway';
18
  src: url('assets/font/Raleway.woff2') format('woff2'), url('assets/font/Raleway.ttf') format('truetype');
 
21
  font-display: swap;
22
  }
23
 
 
24
  @font-face {
25
  font-family: 'Amonk_Outline';
26
  src: url('assets/font/Amonk_Outline.ttf') format('truetype');
 
37
  font-display: swap;
38
  }
39
 
40
+ /* -------------------- Typography -------------------- */
41
+ html {
42
+ box-sizing: border-box;
43
+ }
44
+
45
+ *,
46
+ *::before,
47
+ *::after {
48
+ box-sizing: inherit;
49
+ }
50
+
51
+ h1 {
52
+ font-size: 3vw;
53
+ color: white;
54
+ font-family: 'Super Cartoon';
55
+ }
56
+
57
+ h1,
58
+ h2,
59
+ h3,
60
+ h4,
61
+ h5,
62
+ h6 {
63
+ margin: 0 0 0.6em;
64
+ line-height: 1.2;
65
+ }
66
+
67
+ p {
68
+ line-height: 1.6;
69
+ margin: 0 0 1em;
70
+ }
71
+
72
+ li {
73
+ line-height: 1.6;
74
+ }
75
+
76
+ /* -------------------- Document / Body -------------------- */
77
+ body,
78
+ html {
79
  margin: 0;
80
  padding: 0;
 
81
  height: 100%;
82
  display: flex;
83
  flex-direction: column;
84
+ font-family: Raleway, Roboto, "Helvetica Neue", sans-serif;
85
  background-color: rgb(35 34 32 / 43%);
86
+ }
87
+
88
+ html,
89
+ body {
90
+ overflow-x: hidden;
91
+ }
92
+
93
+
94
+ .imgbgcontainter {
95
+ background-image: url(/assets/images/grammar-bg.png);
96
+ background-size: auto;
97
+ background-position: center;
98
+ background-attachment: fixed;
99
+ width: 100%;
100
+ height: 100%;
101
+ }
102
+
103
+ .grammar-bg {
104
+ position: absolute;
105
+ top: 10%;
106
+ left: 0;
107
+ width: 100%;
108
+ height: auto;
109
+ max-height: calc(100vh - 100px);
110
+ object-fit: fill;
111
+ z-index: -1;
112
+ opacity: 0.2;
113
  }
114
 
115
 
116
 
 
117
 
 
 
118
  .logo {
119
  display: flex;
120
  align-items: center;
121
  gap: 1vw;
122
  }
123
 
124
+
125
  .brand-link {
126
  display: flex;
127
  align-items: center;
128
+ gap: 0.6vw;
129
  text-decoration: none;
130
  }
131
 
 
132
  .product-name {
133
  color: #fff;
134
+ font-size: 2vw;
 
135
  letter-spacing: 0.5px;
136
+
137
  font-family: Amonk_Outline;
138
  line-height: 1;
139
  }
140
 
141
  .brand-link:hover .product-name {
142
+ opacity: 0.9;
143
  }