-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathRecommendation_Letter_Generator.html
More file actions
1449 lines (1308 loc) · 66.1 KB
/
Copy pathRecommendation_Letter_Generator.html
File metadata and controls
1449 lines (1308 loc) · 66.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Recommendation Letter Generator — Genesee Learning Lab</title>
<script src="https://unpkg.com/docx@8.5.0/build/index.js" defer></script>
<link rel="icon" href="favicon.svg" type="image/svg+xml" />
<link rel="stylesheet" href="styles.css" />
<style>
:root {
--bg: #0b0c0f;
--card: #12141a;
--muted: #c4ccda;
--text: #e8ecf2;
--primary: #4f8cff;
--primary-weak: #2a5bd7;
--accent: #22b07d;
--danger: #ff6b6b;
--border: #1e2230;
--shadow: 0 6px 24px rgba(0,0,0,.35), 0 2px 6px rgba(0,0,0,.25);
}
[data-theme="light"] {
--bg: #f5f5f5;
--card: #ffffff;
--muted: #4a4a4a;
--text: #1a1a1a;
--primary: #0052cc;
--primary-weak: #003fa3;
--accent: #28a745;
--danger: #dc3545;
--border: #d0d0d0;
--shadow: 0 2px 8px rgba(0,0,0,.1), 0 1px 3px rgba(0,0,0,.08);
}
[data-theme="high-contrast"] {
--bg: #000;
--card: #000;
--muted: #f2f2f2;
--text: #fff;
--primary: #0ff;
--primary-weak: #0cf;
--accent: #0c0;
--danger: #f00;
--border: #fff;
--shadow: 0 2px 4px rgba(255,255,255,.3);
}
html, body { height: 100%; }
body {
margin: 0;
font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Arial, "Apple Color Emoji", "Segoe UI Emoji";
background: radial-gradient(1200px 800px at 90% -10%, #1a2235 0%, var(--bg) 35%),
radial-gradient(1000px 600px at -10% 110%, #16202e 0%, transparent 40%) var(--bg);
color: var(--text);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
[data-theme="light"] body { background: #f5f5f5; }
[data-theme="high-contrast"] body { background: #000; }
.wrap {
max-width: 860px;
margin: 32px auto;
padding: 0 16px 60px;
}
.sr-only {
position: absolute;
width: 1px; height: 1px;
padding: 0; margin: -1px;
overflow: hidden;
clip: rect(0,0,0,0);
white-space: nowrap;
border: 0;
}
header {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 12px;
margin-bottom: 20px;
flex-wrap: wrap;
}
.title h1 { font-size: 22px; margin: 0; }
.title p { margin: 0; font-size: 13px; color: var(--muted); }
.controls {
display: flex;
flex-wrap: wrap;
gap: 8px;
align-items: flex-start;
justify-content: flex-end;
}
.card {
background: linear-gradient(180deg, rgba(255,255,255,.025), rgba(255,255,255,0));
border: 1px solid var(--border);
border-radius: 16px;
padding: 22px 24px;
box-shadow: var(--shadow);
margin-bottom: 16px;
}
.card-title {
margin: 0 0 16px;
font-size: 15px;
font-weight: 600;
color: var(--primary);
text-transform: uppercase;
letter-spacing: .05em;
}
label {
display: block;
font-size: 13px;
font-weight: 600;
margin-bottom: 5px;
color: var(--muted);
}
input[type="text"],
select,
textarea {
width: 100%;
background: var(--card);
color: var(--text);
border: 1px solid var(--border);
border-radius: 10px;
padding: 10px 12px;
font-size: 14px;
font-family: inherit;
box-sizing: border-box;
}
textarea { resize: vertical; min-height: 90px; }
input[type="text"]:focus,
select:focus,
textarea:focus {
outline: 3px solid var(--primary);
outline-offset: 2px;
}
.form-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 14px;
margin-bottom: 14px;
}
.form-group { margin-bottom: 14px; }
.trait-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
gap: 8px;
}
.trait-item {
display: flex;
align-items: center;
gap: 8px;
background: var(--bg);
border: 1px solid var(--border);
border-radius: 8px;
padding: 8px 10px;
cursor: pointer;
transition: border-color .15s, background .15s;
}
.trait-item:hover { border-color: var(--primary); }
.trait-item input[type="checkbox"] {
width: 16px; height: 16px;
accent-color: var(--primary);
cursor: pointer;
flex-shrink: 0;
}
.trait-item.checked {
border-color: var(--primary);
background: rgba(79,140,255,.08);
}
.trait-label { font-size: 13px; cursor: pointer; }
button {
background: var(--card);
color: var(--text);
border: 1px solid var(--border);
border-radius: 10px;
padding: 10px 18px;
font-size: 14px;
font-family: inherit;
cursor: pointer;
min-height: 44px;
transition: opacity .15s;
}
button:hover { opacity: .85; }
button:focus-visible {
outline: 3px solid var(--primary);
outline-offset: 2px;
}
button.primary {
background: linear-gradient(180deg, var(--primary) 0%, var(--primary-weak) 100%);
border: none;
color: #fff;
font-weight: 600;
}
button:disabled { opacity: .45; cursor: not-allowed; }
.btn-row {
display: flex;
gap: 10px;
flex-wrap: wrap;
margin-top: 8px;
}
#letterOutput {
display: none;
}
.letter-box {
background: var(--card);
border: 1px solid var(--border);
border-radius: 12px;
padding: 32px 36px;
font-size: 15px;
line-height: 1.75;
white-space: pre-wrap;
word-break: break-word;
font-family: Georgia, "Times New Roman", serif;
color: var(--text);
}
[data-theme="light"] .letter-box {
background: #fff;
color: #111;
}
.letter-box p { margin: 0 0 1.2em; }
.notice {
font-size: 12px;
color: var(--muted);
margin-top: 10px;
padding: 10px 14px;
border: 1px solid var(--border);
border-radius: 8px;
background: rgba(255,255,255,.02);
}
.badge-row {
display: flex;
gap: 6px;
flex-wrap: wrap;
margin-top: 6px;
}
.badge {
padding: 2px 8px;
border-radius: 999px;
background: rgba(79,140,255,.12);
border: 1px solid rgba(79,140,255,.25);
color: var(--primary);
font-size: 11px;
font-weight: 600;
}
.divider {
border: none;
border-top: 1px solid var(--border);
margin: 20px 0;
}
@media print {
body { background: #fff !important; color: #000 !important; }
.no-print { display: none !important; }
.letter-box {
border: none !important;
padding: 0 !important;
background: #fff !important;
color: #000 !important;
font-size: 13pt;
line-height: 1.7;
}
.wrap { max-width: 100%; margin: 0; padding: 0; }
}
@media (max-width: 540px) {
.form-row { grid-template-columns: 1fr; }
.letter-box { padding: 20px 18px; }
}
</style>
</head>
<body>
<a class="skip-link" href="#main-content">Skip to main content</a>
<!-- NAVIGATION -->
<nav class="site-nav" aria-label="Main navigation">
<div class="nav-inner">
<a href="index.html" class="nav-brand" aria-label="Genesee Learning Lab — Home">
<span class="nav-brand-icon" aria-hidden="true">GL</span>
Genesee Learning Lab
</a>
<button class="nav-toggle" aria-expanded="false" aria-controls="primary-nav-list" aria-label="Open navigation menu">☰</button>
<ul id="primary-nav-list" class="nav-links" role="list">
<li><a href="index.html">Home</a></li>
<li><a href="projects.html" aria-current="page">Projects</a></li>
<li><a href="programs.html">Programs</a></li>
<li><a href="contact.html">Contact</a></li>
</ul>
</div>
</nav>
<main id="main-content" tabindex="-1">
<div class="wrap">
<header class="no-print">
<div class="title">
<h1>Recommendation Letter Generator</h1>
<p>Generate a personalized, unique letter based on student information.</p>
</div>
<div class="controls">
<button id="themeBtn" aria-label="Switch color theme">🎨 Theme</button>
</div>
</header>
<!-- FORM -->
<section class="card no-print" aria-labelledby="form-heading">
<h2 class="card-title" id="form-heading">Student Information</h2>
<div class="form-row">
<div class="form-group">
<label for="firstName">First Name <span aria-hidden="true">*</span></label>
<input type="text" id="firstName" placeholder="e.g. Jordan" autocomplete="off" required />
</div>
<div class="form-group">
<label for="lastName">Last Name <span aria-hidden="true">*</span></label>
<input type="text" id="lastName" placeholder="e.g. Rivera" autocomplete="off" required />
</div>
</div>
<div class="form-row">
<div class="form-group">
<label for="pronounSelect">Pronouns <span aria-hidden="true">*</span></label>
<select id="pronounSelect" required>
<option value="">— Select pronouns —</option>
<option value="he-him">He/Him</option>
<option value="she-her">She/Her</option>
<option value="they-them">They/Them</option>
<option value="prefer">Prefer not to answer</option>
</select>
</div>
<div></div><!-- spacer: maintains two-column grid for the pronouns row -->
</div>
<div class="form-row">
<div class="form-group">
<label for="classSelect">Class <span aria-hidden="true">*</span></label>
<select id="classSelect">
<option value="">— Select a class —</option>
<option value="intro-cs">Intro to Computer Science</option>
<option value="intro-hw">Intro to Computer Hardware</option>
<option value="ap-csa">AP Computer Science A</option>
<option value="adv-hw">Advanced Computer Hardware</option>
<option value="intro-cyber">Intro to Cybersecurity</option>
<option value="ap-cyber">AP Cybersecurity</option>
<option value="ai-found">AI Foundations</option>
<option value="intro-sd">Intro to Software Development</option>
</select>
</div>
<div class="form-group">
<label for="purposeSelect">Purpose <span aria-hidden="true">*</span></label>
<select id="purposeSelect">
<option value="">— Select a purpose —</option>
<option value="general">General Recommendation</option>
<option value="college">College Application</option>
<option value="scholarship">Scholarship</option>
<option value="internship">Internship</option>
<option value="employment">Employment</option>
<option value="leadership">Leadership Program</option>
<option value="other">Other</option>
</select>
<div id="purposeOtherGroup" style="display:none;margin-top:8px;">
<label for="purposeOther" class="sr-only">Specify purpose</label>
<input type="text" id="purposeOther" placeholder="Describe the opportunity or purpose…" autocomplete="off" />
</div>
</div>
</div>
<hr class="divider" />
<h2 class="card-title" id="traits-heading">Student Traits</h2>
<p style="font-size:13px;color:var(--muted);margin:0 0 12px;">Select two to five traits that best describe this student.</p>
<div class="trait-grid" id="traitGrid" role="group" aria-labelledby="traits-heading">
<!-- populated by JS -->
</div>
<hr class="divider" />
<h2 class="card-title">Optional: Custom Detail</h2>
<div class="form-group">
<label for="customParagraph">Additional context, story, or detail (optional)</label>
<textarea id="customParagraph" placeholder="e.g. Jordan led the class team project to rebuild a server from spare parts, and was the first student to complete the advanced troubleshooting challenge…"></textarea>
</div>
<div class="btn-row">
<button class="primary" id="generateBtn">✉️ Generate Letter</button>
<button id="clearBtn">Clear Form</button>
</div>
<div class="form-group" style="margin-top:14px;">
<label class="trait-item" style="width:auto;display:inline-flex;">
<input type="checkbox" id="includeDigSig" style="width:16px;height:16px;accent-color:var(--primary);cursor:pointer;flex-shrink:0;" />
<span class="trait-label" style="margin-left:8px;">Include digital signature line</span>
</label>
</div>
<p id="formError" role="alert" style="color:var(--danger);font-size:13px;margin-top:8px;display:none;"></p>
</section>
<!-- OUTPUT -->
<section id="letterOutput" aria-labelledby="output-heading">
<div class="card no-print" style="padding:16px 20px;">
<div style="display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;">
<div>
<h2 id="output-heading" style="margin:0 0 4px;font-size:17px;">Generated Letter</h2>
<div class="badge-row" id="outputBadges"></div>
</div>
<div class="btn-row" style="margin:0;">
<button id="regenerateBtn">🔄 Regenerate</button>
<button id="copyBtn">📋 Copy</button>
<button id="printBtn">🖨️ Print</button>
<button id="wordExportBtn">📄 Export Word</button>
</div>
</div>
</div>
<div class="letter-box" id="letterText" aria-live="polite"></div>
<p class="notice no-print">
⚠️ Review this letter before use. Edit as needed to reflect the student's actual accomplishments and your authentic voice as their teacher.
</p>
</section>
</div>
</main>
<!-- FOOTER -->
<footer class="site-footer no-print" aria-labelledby="rec-footer-heading">
<div class="footer-inner">
<h2 id="rec-footer-heading" class="sr-only">Site Footer</h2>
<div class="footer-grid">
<div>
<p class="footer-brand">Genesee Learning Lab</p>
<p class="footer-tagline">Hands-on computer science and technology education for students, families, and communities across Genesee County.</p>
</div>
<nav aria-label="Footer navigation: Site">
<div class="footer-col">
<h4>Site</h4>
<ul role="list">
<li><a href="index.html">Home</a></li>
<li><a href="projects.html">Projects</a></li>
<li><a href="programs.html">Programs</a></li>
<li><a href="contact.html">Contact</a></li>
</ul>
</div>
</nav>
</div>
<div class="footer-bottom">
<span>© 2026 Genesee Learning Lab. All rights reserved.</span>
<span>Built for students, families, and community.</span>
</div>
</div>
</footer>
<script>
// ─── Theme toggle ───────────────────────────────────────────────────────────
const THEMES = ['', 'light', 'high-contrast'];
let themeIdx = 0;
document.getElementById('themeBtn').addEventListener('click', () => {
themeIdx = (themeIdx + 1) % THEMES.length;
const t = THEMES[themeIdx];
document.documentElement.setAttribute('data-theme', t);
document.getElementById('themeBtn').textContent = t === 'light' ? '🌙 Theme' : t === 'high-contrast' ? '🎨 Theme' : '☀️ Theme';
});
// ─── Nav toggle ──────────────────────────────────────────────────────────────
(function () {
const toggle = document.querySelector('.nav-toggle');
const navList = document.getElementById('primary-nav-list');
if (!toggle || !navList) return;
toggle.addEventListener('click', function () {
const isOpen = navList.classList.toggle('is-open');
toggle.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
toggle.setAttribute('aria-label', isOpen ? 'Close navigation menu' : 'Open navigation menu');
});
navList.addEventListener('click', function (e) {
if (e.target.tagName === 'A') {
navList.classList.remove('is-open');
toggle.setAttribute('aria-expanded', 'false');
toggle.setAttribute('aria-label', 'Open navigation menu');
}
});
document.addEventListener('keydown', function (e) {
if (e.key === 'Escape' && navList.classList.contains('is-open')) {
navList.classList.remove('is-open');
toggle.setAttribute('aria-expanded', 'false');
toggle.setAttribute('aria-label', 'Open navigation menu');
toggle.focus();
}
});
}());
// ─── Data ────────────────────────────────────────────────────────────────────
const CLASS_DATA = {
'intro-cs': {
name: 'Intro to Computer Science',
subject: 'computer science',
keywords: ['problem-solving', 'programming foundations', 'logical thinking', 'persistence'],
classDesc: [
'foundational programming and computational thinking',
'introductory computer science concepts and coding fundamentals',
'core principles of programming and algorithmic problem-solving',
],
classAction: [
'building foundational programming skills and learning to think like a developer',
'tackling coding challenges with genuine curiosity and methodical thinking',
'developing the logical mindset that sits at the heart of computer science',
'navigating early programming concepts with persistence and attention to detail',
],
strengthPhrase: [
'demonstrated a natural aptitude for logical thinking and problem decomposition',
'shown a consistent ability to break complex problems into manageable steps',
'approached every programming challenge with patience and a growth mindset',
'built a strong foundation in coding that will serve them well in any technical path',
],
},
'intro-hw': {
name: 'Intro to Computer Hardware',
subject: 'computer hardware',
keywords: ['hardware fundamentals', 'troubleshooting', 'professionalism', 'hands-on technical work'],
classDesc: [
'computer hardware fundamentals and hands-on technical skills',
'hands-on hardware repair, maintenance, and troubleshooting',
'foundational IT support skills and hardware diagnostics',
],
classAction: [
'disassembling, diagnosing, and rebuilding computer systems with care and precision',
'applying structured troubleshooting methods to real hardware problems',
'developing the kind of hands-on technical professionalism that employers look for',
'working confidently with physical components while maintaining a professional workspace',
],
strengthPhrase: [
'demonstrated the technical patience and careful attention to detail that hardware work demands',
'shown an ability to stay organized and methodical under the pressure of live diagnostics',
'brought a professional attitude to every lab exercise, treating each component with respect',
'developed genuine troubleshooting instincts that go beyond simply following a checklist',
],
},
'ap-csa': {
name: 'AP Computer Science A',
subject: 'AP-level computer science',
keywords: ['Java programming', 'abstraction', 'algorithms', 'debugging', 'academic rigor'],
classDesc: [
'rigorous AP Computer Science A curriculum, including Java programming, object-oriented design, and algorithmic thinking',
'college-level Java programming, data structures, and computational problem-solving',
'advanced programming concepts including abstraction, inheritance, and algorithm analysis',
],
classAction: [
'writing well-structured Java programs and reasoning through complex algorithmic problems',
'applying object-oriented design principles to build clean, maintainable code',
'approaching challenging AP-level material with academic discipline and intellectual curiosity',
'debugging intricate programs and refining their solutions under college-course expectations',
],
strengthPhrase: [
'consistently applied strong analytical reasoning to design and debug complex programs',
'demonstrated the kind of academic rigor and precision that AP Computer Science demands',
'shown an impressive capacity to hold multiple layers of abstraction in mind while solving problems',
'excelled at translating high-level problem statements into efficient, well-documented code',
],
},
'adv-hw': {
name: 'Advanced Computer Hardware',
subject: 'advanced hardware and IT systems',
keywords: ['advanced troubleshooting', 'systems thinking', 'repair processes', 'technical independence'],
classDesc: [
'advanced hardware repair, systems analysis, and independent technical work',
'complex diagnostics, component-level repair, and professional IT practices',
'advanced troubleshooting workflows and systems thinking at a professional level',
],
classAction: [
'diagnosing challenging hardware failures and executing multi-step repair processes independently',
'applying systems-level thinking to isolate root causes in complex technical environments',
'demonstrating the kind of technical independence that sets apart serious IT professionals',
'managing repair workflows from diagnosis through documentation with minimal supervision',
],
strengthPhrase: [
'shown a level of technical independence that is rare among students at this stage',
'demonstrated the ability to think holistically about systems, not just individual components',
'approached advanced troubleshooting with the confidence and discipline of a working professional',
'built a repair process mindset that is methodical, thorough, and consistently reliable',
],
},
'intro-cyber': {
name: 'Intro to Cybersecurity',
subject: 'cybersecurity',
keywords: ['security awareness', 'ethical decision-making', 'networks', 'digital responsibility'],
classDesc: [
'foundational cybersecurity concepts including network security, threat awareness, and digital ethics',
'core principles of cybersecurity, including how to identify threats and protect digital systems',
'introductory network security, ethical hacking concepts, and responsible digital practices',
],
classAction: [
'developing security awareness and learning to think like both a defender and an attacker',
'exploring how networks operate and how vulnerabilities can be identified and mitigated',
'applying ethical frameworks to digital decision-making in real-world scenarios',
'building the foundational mindset for responsible cybersecurity practice',
],
strengthPhrase: [
'demonstrated a strong ethical compass alongside genuine technical curiosity',
'shown an ability to balance security concepts with the human responsibility that comes with them',
'approached cybersecurity with the thoughtful, ethics-first mindset that the field demands',
'developed a security-aware perspective that goes well beyond surface-level awareness',
],
},
'ap-cyber': {
name: 'AP Cybersecurity',
subject: 'AP-level cybersecurity',
keywords: ['advanced cybersecurity concepts', 'analysis', 'professionalism', 'risk management'],
classDesc: [
'advanced cybersecurity concepts including threat analysis, risk management, and incident response',
'college-level security frameworks, penetration concepts, and professional cybersecurity practice',
'rigorous AP Cybersecurity content covering analysis, policy, and real-world risk scenarios',
],
classAction: [
'analyzing security threats, evaluating risk, and proposing mitigation strategies at a professional level',
'working through complex cybersecurity scenarios with the analytical depth the AP curriculum demands',
'approaching security problems with both technical skill and a structured, policy-aware mindset',
'developing the professional judgment that effective cybersecurity practitioners rely on daily',
],
strengthPhrase: [
'consistently approached complex security challenges with analytical clarity and professional focus',
'demonstrated the ability to evaluate risk and communicate findings with precision',
'shown a level of cybersecurity maturity that goes well beyond what the course requires',
'brought both technical depth and sound professional judgment to every security analysis',
],
},
'ai-found': {
name: 'AI Foundations',
subject: 'artificial intelligence',
keywords: ['responsible AI use', 'creativity', 'critical thinking', 'data and model awareness'],
classDesc: [
'foundational concepts in artificial intelligence, including responsible use, data literacy, and model awareness',
'how AI systems are built, trained, and applied, with a strong emphasis on ethics and critical thinking',
'AI literacy, creativity with AI tools, and the critical thinking skills needed to evaluate AI outputs responsibly',
],
classAction: [
'exploring how AI systems work and thinking critically about how they should be used',
'applying creative thinking to AI-assisted projects while maintaining a responsibility-first perspective',
'developing the data and model awareness that distinguishes thoughtful AI users from passive ones',
'engaging with AI tools in ways that reflect genuine curiosity, creativity, and ethical consideration',
],
strengthPhrase: [
'demonstrated a nuanced, critical perspective on AI technology that many adults have yet to develop',
'shown a unique combination of creativity and analytical rigor in applying AI tools responsibly',
'approached AI with the kind of thoughtful skepticism and intellectual honesty the field requires',
'brought genuine curiosity and a strong ethical framework to every AI-related discussion and project',
],
},
'intro-sd': {
name: 'Intro to Software Development',
subject: 'software development',
keywords: ['software design', 'collaboration', 'testing', 'user-focused problem-solving'],
classDesc: [
'foundational software development skills including design, testing, and collaborative coding practices',
'the software development lifecycle, from user-centered design through testing and iteration',
'core software engineering concepts including version control, collaboration, and writing testable code',
],
classAction: [
'designing and building software projects with real users in mind from the very first step',
'collaborating on codebases while maintaining quality through systematic testing and review',
'applying the software development lifecycle with discipline, creativity, and a team-first attitude',
'thinking about user needs and edge cases in a way that produces cleaner, more reliable software',
],
strengthPhrase: [
'demonstrated a user-centered approach to software design that sets strong engineers apart',
'shown a collaborative spirit and a commitment to code quality that makes every team better',
'brought both creative vision and methodical discipline to every stage of the development process',
'developed a testing mindset early, understanding that great software is both built and verified with care',
],
},
};
const TRAITS_LIST = [
{ id: 'hardworking', label: 'Hardworking' },
{ id: 'creative', label: 'Creative' },
{ id: 'dependable', label: 'Dependable' },
{ id: 'curious', label: 'Intellectually Curious' },
{ id: 'collaborative', label: 'Collaborative' },
{ id: 'leadership', label: 'Natural Leader' },
{ id: 'resilient', label: 'Resilient' },
{ id: 'detail-oriented', label: 'Detail-Oriented' },
{ id: 'growth-mindset', label: 'Growth Mindset' },
{ id: 'communicator', label: 'Strong Communicator' },
{ id: 'self-starter', label: 'Self-Starter' },
{ id: 'empathetic', label: 'Empathetic' },
{ id: 'focused', label: 'Focused Under Pressure' },
{ id: 'organized', label: 'Organized' },
{ id: 'enthusiastic', label: 'Enthusiastic' },
];
const TRAIT_PHRASES = {
'hardworking': [
'a willingness to put in the work that others often avoid',
'a work ethic that consistently sets the tone for the entire class',
'the kind of industriousness that turns difficult tasks into accomplished goals',
'an ability to stay committed long after the initial excitement fades',
],
'creative': [
'a creative sensibility that surfaces in every project that comes {P_POSS} way',
'a knack for approaching problems from unexpected, often more effective angles',
'the imagination to see solutions that aren\'t obvious to everyone else',
'a creative instinct that makes {P_POSS} work distinctively {P_POSS} own',
],
'dependable': [
'a level of dependability that both peers and teachers can count on without question',
'consistent follow-through that makes {P_OBJ} the kind of person you want on any team',
'reliability that is simply part of {P_POSS} character, never something {P_SUBJ} {P_HAS} to perform',
'a track record of following through on commitments, without needing reminders',
],
'curious': [
'an intellectual curiosity that keeps {P_OBJ} asking questions long after the lesson ends',
'a genuine hunger to understand the "why" behind every concept, not just the "what"',
'the kind of restless curiosity that turns course material into a personal investigation',
'an inquisitive mind that elevates classroom discussions for everyone around {P_OBJ}',
],
'collaborative': [
'an instinct for bringing people together and making group work genuinely productive',
'the ability to listen, adapt, and contribute in ways that strengthen any team',
'a collaborative spirit that makes others more effective simply by being present',
'a consistent effort to lift teammates up and elevate the group rather than stand apart',
],
'leadership': [
'a quiet but unmistakable leadership presence that others naturally gravitate toward',
'the ability to guide a group without overshadowing the contributions of others',
'leadership qualities that show up in every group interaction and collaborative moment',
'the kind of leadership that earns respect through example rather than authority',
],
'resilient': [
'a resilience that allows {P_OBJ} to treat setbacks as data rather than failures',
'the ability to reset after a difficult challenge and come back stronger each time',
'a composure under pressure that lets {P_OBJ} stay productive when others disengage',
'the persistence to keep going even when the path forward is unclear',
],
'detail-oriented': [
'a meticulous attention to detail that catches what others miss',
'the kind of precision that elevates the quality of everything in {P_POSS} work',
'a habit of reviewing and refining work that reflects genuine pride in the outcome',
'a careful, systematic approach that makes {P_POSS} completed work notably thorough',
],
'growth-mindset': [
'a growth mindset that treats every mistake as an opportunity to improve',
'the awareness to recognize where {P_SUBJ} can grow and the drive to act on it',
'an openness to feedback that makes {P_OBJ} one of the most coachable students I\'ve taught',
'a belief in {P_POSS} own ability to improve that translates into real, visible progress',
],
'communicator': [
'a rare ability to explain technical ideas clearly to a wide range of audiences',
'strong communication skills that make complex topics accessible and engaging',
'the confidence to share ideas and ask questions in ways that move conversations forward',
'clarity in both written and verbal communication that most students are still working toward',
],
'self-starter': [
'a natural initiative that means {P_SUBJ} {P_IS} often working before the instructions are finished',
'the ability to identify what needs to be done and begin without waiting to be told',
'an intrinsic motivation that sets {P_OBJ} apart from peers who need frequent prompting',
'a proactive energy that consistently raises the bar for everyone working alongside {P_OBJ}',
],
'empathetic': [
'a genuine empathy that shapes every interaction and collaborative moment',
'the social awareness to understand how {P_POSS} actions affect others, and to care about that',
'an ability to see problems through multiple perspectives, which consistently improves {P_POSS} solutions',
'a warmth and consideration for others that makes {P_OBJ} a stabilizing presence in any group',
],
'focused': [
'an ability to maintain focus and output quality even when the environment around {P_OBJ} is noisy or uncertain',
'the composure to prioritize effectively when multiple demands compete for attention',
'a mental discipline that lets {P_OBJ} stay on task when others have long since drifted',
'the kind of sustained concentration that produces work of consistently high quality',
],
'organized': [
'an organizational discipline that keeps {P_POSS} work clean, accessible, and always on schedule',
'a systematic approach to managing materials and time that others in the class often look to as a model',
'the ability to impose structure on complex projects without losing sight of the bigger picture',
'a natural tendency to plan ahead that means {P_SUBJ} {P_IS} rarely scrambling at the last minute',
],
'enthusiastic': [
'an enthusiasm for the subject that is contagious and genuinely raises the energy of any room {P_SUBJ} {P_IS} in',
'a visible excitement about learning that makes classroom discussions more dynamic and engaging',
'a passion for this field that comes through in everything from {P_POSS} questions to {P_POSS} project choices',
'the kind of authentic enthusiasm that signals a student who has found something {P_SUBJ} {P_IS} truly passionate about',
],
};
const PURPOSE_DATA = {
'general': {
label: 'General Recommendation',
salutation: 'To Whom It May Concern',
openingContext: [
'to offer my enthusiastic recommendation for {name}',
'to recommend {name} as a student of exceptional character and ability',
'in strong support of {name}, who has made a genuine impression on me as their teacher',
],
closingStatement: [
'I recommend {name} with confidence for future academic, professional, and leadership opportunities.',
'Whoever has the opportunity to work with {name} next will be fortunate. I recommend {P_OBJ} fully and without reservation.',
'I recommend {name} with confidence for future academic, professional, and leadership opportunities, and I do so proudly.',
'{name} is exactly the kind of person any school, employer, or program would be glad to welcome. I offer this recommendation wholeheartedly.',
],
},
'college': {
label: 'College Application',
salutation: 'Dear Admissions Committee',
openingContext: [
'in enthusiastic support of {name}\'s application for admission',
'to recommend {name} as a candidate for your institution',
'on behalf of {name} as they pursue admission to your program',
],
closingStatement: [
'{name} will contribute meaningfully to your campus community, both inside the classroom and beyond it.',
'Your institution will be fortunate to have {name}, someone whose curiosity and drive will make your campus community stronger.',
'I have no reservations whatsoever in recommending {name} for admission, and I look forward to following {P_POSS} continued success.',
'{name} is the kind of student who will make the most of every opportunity your institution offers, and then some.',
],
},
'scholarship': {
label: 'Scholarship',
salutation: 'Dear Scholarship Selection Committee',
openingContext: [
'in support of {name}\'s application for this scholarship',
'to recommend {name} for this scholarship opportunity',
'on behalf of {name} as they apply for this scholarship',
],
closingStatement: [
'I am confident that {name} will make excellent use of this opportunity and reflect the values this scholarship was created to honor.',
'{name} represents exactly the kind of student this scholarship was designed to support, someone whose potential is matched only by {P_POSS} commitment to {P_POSS} own growth.',
'This scholarship would be in excellent hands with {name}, and I cannot recommend {P_OBJ} highly enough for this recognition.',
'Investing in {name} through this scholarship is investing in someone who will return that investment many times over.',
],
},
'internship': {
label: 'Internship',
salutation: 'Dear Hiring Team',
openingContext: [
'to recommend {name} for this internship opportunity',
'in support of {name}\'s application for this internship position',
'on behalf of {name} as they pursue this internship',
],
closingStatement: [
'{name} is ready for a real professional environment. {P_SUBJ_CAP} {P_IS} technically capable, grounded, and genuinely eager to learn on the job.',
'Any team that works with {name} will quickly see what I already know: this is a student worth investing in.',
'I recommend {name} without reservation for this internship and am confident {P_SUBJ} will exceed your expectations.',
'{name} brings both the technical foundation and the professional maturity to make a real contribution from day one.',
],
},
'employment': {
label: 'Employment',
salutation: 'Dear Hiring Team',
openingContext: [
'to recommend {name} for employment consideration',
'in support of {name}\'s application for this position',
'on behalf of {name} as they pursue this professional opportunity',
],
closingStatement: [
'{name} is work-ready in the truest sense, technically capable, professionally grounded, and eager to contribute from day one.',
'Any organization that brings {name} on board will quickly discover what I already know: this is someone worth investing in.',
'I recommend {name} without reservation for this role and am confident {P_SUBJ} will meet and exceed your expectations.',
'{name} combines technical skill and professional maturity that will help {P_OBJ} hit the ground running in almost any workplace.',
],
},
'leadership': {
label: 'Leadership Program',
salutation: 'Dear Selection Committee',
openingContext: [
'to support {name}\'s application to your leadership program',
'in recommendation of {name} for this leadership opportunity',
'on behalf of {name} as they apply to this leadership program',
],
closingStatement: [
'{name} is prepared not just to participate in a leadership program, but to grow through it in ways that will ripple outward for years.',
'Your program would benefit greatly from what {name} brings, and {name} would benefit equally from the challenges your program offers.',
'I am confident that {name} will not only meet the bar this program sets. {P_SUBJ_CAP} will raise it.',
'Selecting {name} for this program means choosing someone who will make the program proud.',
],
},
'other': {
label: 'Other',
salutation: 'Dear Selection Committee',
openingContext: [
'in strong support of {name} for this opportunity',
'to recommend {name} for this upcoming opportunity',
'on behalf of {name} as they pursue this next step',
],
closingStatement: [
'I recommend {name} fully and without reservation, and I look forward to hearing about {P_POSS} continued growth.',
'{name} has more than earned this opportunity, and I am proud to put {P_POSS} name forward.',
'I hold {name} in the highest regard and offer this recommendation with genuine enthusiasm.',
'{name} is exactly the kind of person who makes the most of every opportunity given to {P_OBJ}.',
],
},
};
const OPENINGS = [
(name, cls, purposeCtx, p) =>
`It is my genuine pleasure to write this letter ${purposeCtx}. As ${name}'s teacher in ${cls}, I have had the opportunity to watch ${p.obj} grow in ways that go well beyond the technical content of the course.`,
(name, cls, purposeCtx, p) =>
`I am writing ${purposeCtx}, and I do so with genuine enthusiasm. ${name} is one of those students who reminds you why teaching matters, someone whose growth in ${cls} has been both impressive and deeply satisfying to witness.`,
(name, cls, purposeCtx, p) =>
`Without hesitation, I am writing to offer my strongest recommendation ${purposeCtx}. Having worked closely with ${name} in ${cls}, I have developed a clear and confident picture of who ${p.subj} ${p.is} as both a student and a person.`,
(name, cls, purposeCtx, p) =>
`I have had the privilege of teaching ${name} in ${cls}, and this letter ${purposeCtx} comes from a place of genuine admiration. ${name} consistently demonstrated qualities that set ${p.obj} apart, not just in my class, but as a person.`,
(name, cls, purposeCtx, p) =>
`Rarely do I encounter a student who leaves such a lasting impression in a single semester, but ${name} has done exactly that. I am writing ${purposeCtx} with full confidence that my recommendation reflects not just ${p.poss} performance in ${cls}, but ${p.poss} broader potential.`,
(name, cls, purposeCtx, p) =>
`Some students show up, do the work, and leave. ${name} did something different. In ${cls}, ${p.subjCap} showed me what it looks like when a student genuinely invests in ${p.poss} own growth, and I am glad to write this letter ${purposeCtx}.`,
(name, cls, purposeCtx, p) =>
`I don't write recommendation letters casually, but writing this one ${purposeCtx} is easy. ${name} has been one of the most memorable students I have taught in ${cls}, and that is not something I say often.`,
];
const TRANSITIONS = [
'Beyond {P_POSS} technical abilities,',
'What sets {name} apart, however, is more than coursework performance.',
'Equally important to {P_POSS} technical growth has been {P_POSS} character.',
'The technical skills tell only part of the story.',
'Academic performance, of course, only begins to describe {name}.',
'What I find most compelling about {name}, though, is not captured in grades alone.',
'There is something worth noting that goes beyond what any grade reflects.',
];
const CLOSINGS = [
(name, p) =>
`I hold ${name} in the highest regard and offer this recommendation wholeheartedly. Please feel free to contact me if you would like to discuss ${p.poss} qualifications in greater detail.`,
(name, p) =>
`It is rare to recommend a student with this level of certainty, and I do so for ${name} without reservation. I am happy to speak further should it be helpful.`,
(name, p) =>
`I recommend ${name} with confidence and pride. Should you have any questions or wish to speak with me directly, I welcome the conversation.`,
(name, p) =>
`My support for ${name} is unconditional. ${p.subjCap} ${p.has} more than earned this opportunity, and I look forward to hearing of ${p.poss} continued success.`,
(name, p) =>
`Thank you for your consideration of ${name}. I am available to provide any additional context and would be glad to speak with you at your convenience.`,
(name, p) =>
`Teaching ${name} has been a genuinely rewarding experience, and I have every confidence in where ${p.subj} ${p.is} headed. Please reach out if I can be of any further help.`,
];
// ─── Utilities ──────────────────────────────────────────────────────────────
function pick(arr) {
return arr[Math.floor(Math.random() * arr.length)];
}
function pickN(arr, n) {
const shuffled = [...arr];
for (let i = shuffled.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
}
return shuffled.slice(0, Math.min(n, shuffled.length));
}
function capitalize(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
// Returns a pronoun set for use throughout generated letters
function getPronouns(selection, firstName) {
if (selection === 'he-him') {
return { subj: 'he', obj: 'him', poss: 'his', subjCap: 'He', objCap: 'Him', possCap: 'His', is: 'is', has: 'has' };
}
if (selection === 'she-her') {
return { subj: 'she', obj: 'her', poss: 'her', subjCap: 'She', objCap: 'Her', possCap: 'Her', is: 'is', has: 'has' };
}
if (selection === 'prefer') {