Ryadg commited on
Commit
ee14d3e
Β·
1 Parent(s): b5f4a07

feat: full-height libraries, pre-generated reward image, English upload text

Browse files
Files changed (2) hide show
  1. app.py +97 -25
  2. ui/index.html +36 -23
app.py CHANGED
@@ -217,6 +217,27 @@ body { background: #0A0F1E !important; margin: 0 !important; }
217
  opacity: 0 !important;
218
  }
219
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
220
  /* ── Load PDF button β€” defeat Gradio's orange primary override ──────────────── */
221
  .gr-button-primary,
222
  button.primary,
@@ -414,7 +435,7 @@ BRIDGE_JS = """() => {
414
  mode: 'open',
415
  mcqData: null,
416
  waitingMCQ: false, prevMCQ: '',
417
- imgPoll: null,
418
  };
419
  const $ = id => document.getElementById(id);
420
 
@@ -498,6 +519,44 @@ BRIDGE_JS = """() => {
498
  })();
499
  }
500
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
501
  // ── Background particles (canvas is in gr.HTML markup; animation here) ────
502
  function initParticles() {
503
  const canvas = $('particles');
@@ -592,39 +651,48 @@ BRIDGE_JS = """() => {
592
  return verdict;
593
  }
594
 
595
- // ── Session image (shown in the End Session modal) ────────────────────────
 
 
 
 
 
 
 
 
 
 
596
  function stopSessionImagePoll() {
597
  if (S.imgPoll) { clearInterval(S.imgPoll); S.imgPoll = null; }
598
  $('modal-image-loading')?.classList.add('hidden');
599
  }
600
 
601
- function startSessionImage() {
602
  const sec = $('modal-session-image'), loading = $('modal-image-loading'), img = $('session-image');
603
  if (!sec || !loading || !img) return;
604
  stopSessionImagePoll();
605
  sec.classList.add('hidden'); sec.classList.remove('fade-in-up');
606
- if (S.history.length === 0) return;
607
- const topics = S.history.slice(0, 3).map(h => h.question).join(', ');
608
- if (!setGradioTA('#hidden-session-topics textarea', topics)) return;
609
- const baseline = document.querySelector('#hidden-concept-image img')?.src || '';
 
 
 
610
  loading.classList.remove('hidden');
611
- // Small delay so Gradio registers the topics input before the click
612
- setTimeout(() => {
613
- if (!clickGradioBtn('#hidden-generate-session-image-btn')) { stopSessionImagePoll(); return; }
614
- const started = Date.now();
615
- S.imgPoll = setInterval(() => {
616
- const el = document.querySelector('#hidden-concept-image img');
617
- if (el && el.src && el.src !== baseline) {
618
- stopSessionImagePoll();
619
- img.src = el.src;
620
- sec.classList.remove('hidden');
621
- void sec.offsetWidth; // restart fade-in animation
622
- sec.classList.add('fade-in-up');
623
- } else if (Date.now() - started > 120000) {
624
- stopSessionImagePoll(); // give up quietly after 2 min
625
- }
626
- }, 500);
627
- }, 200);
628
  }
629
 
630
  // ── Question display ───────────────────────────────────────────────────────
@@ -645,6 +713,7 @@ BRIDGE_JS = """() => {
645
  if (nq) nq.disabled = false;
646
  $('mcq-options')?.classList.add('hidden');
647
  $('answer-wrapper')?.classList.remove('hidden');
 
648
  }
649
 
650
  // ── Language toggle ────────────────────────────────────────────────────────
@@ -686,6 +755,7 @@ BRIDGE_JS = """() => {
686
  }
687
  }
688
  $('mcq-options')?.classList.remove('hidden');
 
689
  $('mcq-explanations')?.classList.add('hidden');
690
  $('answer-wrapper')?.classList.add('hidden');
691
  $('feedback-card')?.classList.add('hidden');
@@ -791,7 +861,7 @@ BRIDGE_JS = """() => {
791
  return '<div class="modal-history-item"><span class="hist-badge '+vc+'">'+(vc==='correct'?'βœ“':vc==='partial'?'~':'βœ—')+'</span><span class="hist-q">'+h.question+'</span></div>';
792
  }).join('');
793
  }
794
- startSessionImage();
795
  modal.classList.remove('hidden');
796
  }
797
 
@@ -841,6 +911,8 @@ BRIDGE_JS = """() => {
841
  wireButtons();
842
  initHeroProfessor();
843
  initParticles();
 
 
844
 
845
  const statusEl = document.querySelector('#hidden-status-output textarea');
846
  if (statusEl && statusEl.value) applyStatus(statusEl.value);
 
217
  opacity: 0 !important;
218
  }
219
 
220
+ /* ── Empty .form wrappers ───────────────────────────────────────────────────────
221
+ Gradio groups consecutive textboxes into bordered .form containers; ours are
222
+ all parked off-screen, leaving 3 empty bars at the page bottom β€” hide them. */
223
+ .form:has(> #hidden-answer-input),
224
+ .form:has(> #hidden-feedback-output),
225
+ .form:has(> #hidden-question-trigger),
226
+ .form:has(> #hidden-session-topics) {
227
+ position: fixed !important;
228
+ top: -9999px !important;
229
+ left: -9999px !important;
230
+ width: 1px !important;
231
+ height: 1px !important;
232
+ border: none !important;
233
+ }
234
+ /* fallback for browsers without :has β€” at least make the bars invisible */
235
+ .gradio-container .form {
236
+ background: transparent !important;
237
+ border-color: transparent !important;
238
+ box-shadow: none !important;
239
+ }
240
+
241
  /* ── Load PDF button β€” defeat Gradio's orange primary override ──────────────── */
242
  .gr-button-primary,
243
  button.primary,
 
435
  mode: 'open',
436
  mcqData: null,
437
  waitingMCQ: false, prevMCQ: '',
438
+ imgPoll: null, imgRequested: false,
439
  };
440
  const $ = id => document.getElementById(id);
441
 
 
519
  })();
520
  }
521
 
522
+ // ── Library wings: span the whole page, repeat content to the bottom ──────
523
+ // position:fixed breaks under Gradio's ancestor transforms, so the wings are
524
+ // re-parented onto <body>, stretched to the page height, and their .lib-unit
525
+ // content is cloned as many times as needed to fill it.
526
+ function syncLibraryWings() {
527
+ const wings = document.querySelectorAll('.library');
528
+ if (!wings.length) return;
529
+ wings.forEach(w => { if (w.parentElement !== document.body) document.body.appendChild(w); });
530
+ const gc = document.querySelector('.gradio-container');
531
+ const H = Math.max(gc ? gc.scrollHeight : 0, window.innerHeight);
532
+ wings.forEach(w => {
533
+ if (parseInt(w.style.height, 10) !== H) w.style.height = H + 'px';
534
+ const first = w.querySelector('.lib-unit');
535
+ if (!first) return;
536
+ const per = first.offsetHeight || 950;
537
+ const need = Math.max(1, Math.ceil(H / per));
538
+ let units = w.querySelectorAll('.lib-unit');
539
+ for (let i = units.length; i < need; i++) w.appendChild(first.cloneNode(true));
540
+ units = w.querySelectorAll('.lib-unit');
541
+ for (let i = units.length - 1; i >= need; i--) units[i].remove();
542
+ });
543
+ }
544
+
545
+ // ── Force the Gradio upload zone to English (it localises to the browser) ──
546
+ function enforceEnglishUpload() {
547
+ const row = document.querySelector('#upload-row');
548
+ if (!row || !/D\\u00e9posez|Cliquez pour|Glissez/.test(row.textContent)) return;
549
+ const tw = document.createTreeWalker(row, NodeFilter.SHOW_TEXT);
550
+ let n;
551
+ while ((n = tw.nextNode())) {
552
+ let t = n.nodeValue;
553
+ t = t.replace(/(D\\u00e9posez|Glissez-d\\u00e9posez) le fichier ici/g, 'Drop your PDF here');
554
+ t = t.replace(/-\\s*ou\\s*-/g, '- or -');
555
+ t = t.replace(/Cliquez pour t\\u00e9l\\u00e9(charger|verser) un fichier/g, 'Click to upload');
556
+ if (t !== n.nodeValue) n.nodeValue = t;
557
+ }
558
+ }
559
+
560
  // ── Background particles (canvas is in gr.HTML markup; animation here) ────
561
  function initParticles() {
562
  const canvas = $('particles');
 
651
  return verdict;
652
  }
653
 
654
+ // ── Session reward image (generic encouraging illustration) ───────────────
655
+ // Pre-generated in the background as soon as the first question shows, so
656
+ // the End Session modal can display it instantly.
657
+ function requestSessionImage() {
658
+ if (S.imgRequested) return;
659
+ if (!setGradioTA('#hidden-session-topics textarea', 'study session')) return;
660
+ S.imgRequested = true;
661
+ // Small delay so Gradio registers the input before the click
662
+ setTimeout(() => clickGradioBtn('#hidden-generate-session-image-btn'), 200);
663
+ }
664
+
665
  function stopSessionImagePoll() {
666
  if (S.imgPoll) { clearInterval(S.imgPoll); S.imgPoll = null; }
667
  $('modal-image-loading')?.classList.add('hidden');
668
  }
669
 
670
+ function showSessionImage() {
671
  const sec = $('modal-session-image'), loading = $('modal-image-loading'), img = $('session-image');
672
  if (!sec || !loading || !img) return;
673
  stopSessionImagePoll();
674
  sec.classList.add('hidden'); sec.classList.remove('fade-in-up');
675
+ const ready = document.querySelector('#hidden-concept-image img');
676
+ if (ready && ready.src) { // preloaded β€” show instantly
677
+ img.src = ready.src;
678
+ sec.classList.remove('hidden');
679
+ return;
680
+ }
681
+ requestSessionImage(); // fallback in case the preload never fired
682
  loading.classList.remove('hidden');
683
+ const started = Date.now();
684
+ S.imgPoll = setInterval(() => {
685
+ const el = document.querySelector('#hidden-concept-image img');
686
+ if (el && el.src) {
687
+ stopSessionImagePoll();
688
+ img.src = el.src;
689
+ sec.classList.remove('hidden');
690
+ void sec.offsetWidth; // restart fade-in animation
691
+ sec.classList.add('fade-in-up');
692
+ } else if (Date.now() - started > 120000) {
693
+ stopSessionImagePoll(); // give up quietly after 2 min
694
+ }
695
+ }, 500);
 
 
 
 
696
  }
697
 
698
  // ── Question display ───────────────────────────────────────────────────────
 
713
  if (nq) nq.disabled = false;
714
  $('mcq-options')?.classList.add('hidden');
715
  $('answer-wrapper')?.classList.remove('hidden');
716
+ requestSessionImage(); // generate the reward image while the user answers
717
  }
718
 
719
  // ── Language toggle ────────────────────────────────────────────────────────
 
755
  }
756
  }
757
  $('mcq-options')?.classList.remove('hidden');
758
+ requestSessionImage(); // generate the reward image while the user answers
759
  $('mcq-explanations')?.classList.add('hidden');
760
  $('answer-wrapper')?.classList.add('hidden');
761
  $('feedback-card')?.classList.add('hidden');
 
861
  return '<div class="modal-history-item"><span class="hist-badge '+vc+'">'+(vc==='correct'?'βœ“':vc==='partial'?'~':'βœ—')+'</span><span class="hist-q">'+h.question+'</span></div>';
862
  }).join('');
863
  }
864
+ showSessionImage();
865
  modal.classList.remove('hidden');
866
  }
867
 
 
911
  wireButtons();
912
  initHeroProfessor();
913
  initParticles();
914
+ syncLibraryWings();
915
+ enforceEnglishUpload();
916
 
917
  const statusEl = document.querySelector('#hidden-status-output textarea');
918
  if (statusEl && statusEl.value) applyStatus(statusEl.value);
ui/index.html CHANGED
@@ -218,17 +218,24 @@
218
  .professor:hover .prof-arm.right { animation: prof-wave .8s ease-in-out infinite; }
219
 
220
  /* ── Library wings β€” full-height public-library decor on both edges ──── */
 
 
 
221
  .library {
222
- position: fixed; top: 0; bottom: 0; z-index: 0;
223
  /* stretch right up to the 760px centre column (20px breathing room) */
224
  width: max(190px, calc((100vw - 800px) / 2));
225
- display: flex; flex-direction: column; justify-content: space-between;
226
- gap: 12px; padding: 26px 18px 0;
227
  background:
228
  radial-gradient(ellipse 90% 16% at 50% -3%, rgba(167,139,250,.10), transparent 60%),
229
  linear-gradient(180deg, #0B1126 0%, #0A0F1E 55%, #0C1226 100%);
230
  overflow: hidden;
231
- animation: fadeIn .8s .35s ease both;
 
 
 
 
 
232
  }
233
  .library-left { left: 0; border-right: 1px solid var(--border); box-shadow: 18px 0 40px -20px rgba(0,0,0,.6); }
234
  .library-right { right: 0; border-left: 1px solid var(--border); box-shadow: -18px 0 40px -20px rgba(0,0,0,.6); }
@@ -879,8 +886,11 @@
879
  </style>
880
  <canvas id="particles"></canvas>
881
 
882
- <!-- ── Library wings β€” full-height decor flanking the app (decorative) ──── -->
 
 
883
  <div class="library library-left" aria-hidden="true">
 
884
  <div class="lib-shelf">
885
  <i class="bk b2" style="--h:36px"></i><i class="bk b3" style="--h:50px"></i><i class="bk b1 slim" style="--h:56px"></i><i class="bk b6" style="--h:40px"></i>
886
  <i class="bk b1 lean" style="--h:34px"></i><i class="bk b6" style="--h:34px"></i><i class="bk b7 slim" style="--h:44px"></i><i class="bk b4" style="--h:46px"></i>
@@ -904,13 +914,13 @@
904
  <div class="lib-aisle"><div class="aisle-end"></div><span class="aisle-bot"></span></div>
905
  <div class="lib-tall">
906
  <div class="lib-shelf pad-r">
907
- <i class="bk b2" style="--h:42px"></i><i class="bk b8" style="--h:52px"></i><i class="bk b5 slim" style="--h:32px"></i><i class="bk b7" style="--h:44px"></i>
908
- <i class="bk b2" style="--h:54px"></i><i class="bk b3 wide" style="--h:52px"></i><i class="bk b7 slim" style="--h:50px"></i><i class="bk b3" style="--h:48px"></i>
909
- <i class="bk b7" style="--h:56px"></i><i class="bk b5 lean" style="--h:34px"></i><i class="bk b3" style="--h:44px"></i><i class="bk b2" style="--h:28px"></i>
910
- <i class="bk b1 slim" style="--h:28px"></i><i class="bk b3" style="--h:28px"></i><i class="bk b8 lean" style="--h:44px"></i><i class="bk b6" style="--h:44px"></i>
911
- <i class="bk b2 slim" style="--h:56px"></i><i class="bk b6" style="--h:34px"></i><i class="bk b8" style="--h:40px"></i><i class="bk b2 lean" style="--h:48px"></i>
912
- <i class="bk b5" style="--h:54px"></i><i class="bk b7" style="--h:48px"></i><i class="bk b6" style="--h:40px"></i><i class="bk b7" style="--h:54px"></i>
913
- <i class="bk b1 lean" style="--h:34px"></i><i class="bk b3" style="--h:36px"></i><i class="bk b5" style="--h:56px"></i><i class="bk b1" style="--h:54px"></i>
914
  </div>
915
  <div class="lib-ladder"></div>
916
  <div class="mini-bot bot-climber"><div class="mini-head"><span class="mini-eye"></span><span class="mini-eye"></span></div><div class="mini-body"></div></div>
@@ -923,9 +933,11 @@
923
  <div class="lib-floor">
924
  <div class="mini-bot bot-walker"><div class="mini-head"><span class="mini-eye"></span><span class="mini-eye"></span></div><div class="mini-body"></div></div>
925
  </div>
 
926
  </div>
927
 
928
  <div class="library library-right" aria-hidden="true">
 
929
  <div class="lib-shelf pad-r">
930
  <i class="bk b2" style="--h:44px"></i><i class="bk b8" style="--h:30px"></i><i class="bk b7 slim" style="--h:32px"></i><i class="bk b4" style="--h:34px"></i>
931
  <i class="bk b8 lean" style="--h:28px"></i><i class="bk b1 wide" style="--h:36px"></i><i class="bk b8 slim" style="--h:36px"></i><i class="bk b4 slim" style="--h:50px"></i>
@@ -949,13 +961,13 @@
949
  <div class="lib-aisle"><div class="aisle-end"></div><span class="aisle-bot" style="animation-delay:-4s"></span></div>
950
  <div class="lib-tall">
951
  <div class="lib-shelf pad-l" style="justify-content:flex-end">
952
- <i class="bk b7 slim" style="--h:54px"></i><i class="bk b6 slim" style="--h:36px"></i><i class="bk b2 slim" style="--h:30px"></i><i class="bk b5 wide" style="--h:54px"></i>
953
- <i class="bk b4" style="--h:54px"></i><i class="bk b3" style="--h:52px"></i><i class="bk b8" style="--h:28px"></i><i class="bk b6" style="--h:38px"></i>
954
- <i class="bk b3 wide" style="--h:52px"></i><i class="bk b4 lean" style="--h:44px"></i><i class="bk b8" style="--h:28px"></i><i class="bk b1 wide" style="--h:36px"></i>
955
- <i class="bk b3" style="--h:36px"></i><i class="bk b1" style="--h:40px"></i><i class="bk b4 slim lean" style="--h:38px"></i><i class="bk b6" style="--h:56px"></i>
956
- <i class="bk b5" style="--h:28px"></i><i class="bk b7" style="--h:44px"></i><i class="bk b8" style="--h:50px"></i><i class="bk b6 slim lean" style="--h:34px"></i>
957
- <i class="bk b1 wide" style="--h:56px"></i><i class="bk b4 slim" style="--h:48px"></i><i class="bk b8" style="--h:56px"></i><i class="bk b3" style="--h:48px"></i>
958
- <i class="bk b5 lean" style="--h:50px"></i><i class="bk b2" style="--h:48px"></i><i class="bk b5 slim" style="--h:56px"></i><i class="bk b2 slim" style="--h:36px"></i>
959
  </div>
960
  <div class="mini-bot bot-fetcher"><div class="mini-head"><span class="mini-eye"></span><span class="mini-eye"></span></div><div class="mini-body"></div></div>
961
  </div>
@@ -970,6 +982,7 @@
970
  <div class="mini-head"><span class="mini-eye"></span><span class="mini-eye"></span></div><div class="mini-body"></div>
971
  </div>
972
  </div>
 
973
  </div>
974
 
975
  <div id="app">
@@ -1144,12 +1157,12 @@
1144
  <p class="modal-message" id="modal-message"></p>
1145
  <div class="modal-history hidden" id="modal-history"></div>
1146
  <div class="loading-msg hidden" id="modal-image-loading">
1147
- <span class="spinner"></span> Generating your visual summary…
1148
  </div>
1149
  <div class="hidden" id="modal-session-image">
1150
- <div class="concept-image-title">Visual Summary</div>
1151
- <img id="session-image" alt="Visual summary of your session" />
1152
- <p class="concept-image-caption">Visual summary of your session</p>
1153
  </div>
1154
  <button class="btn-primary" id="modal-close" style="width:100%;justify-content:center">
1155
  Study Again
 
218
  .professor:hover .prof-arm.right { animation: prof-wave .8s ease-in-out infinite; }
219
 
220
  /* ── Library wings β€” full-height public-library decor on both edges ──── */
221
+ /* Positioned absolute (not fixed: Gradio ancestor transforms break fixed)
222
+ and re-parented onto <body> by BRIDGE_JS, which also stretches the
223
+ height to the full page and clones .lib-unit blocks to fill it. */
224
  .library {
225
+ position: absolute; top: 0; z-index: 0;
226
  /* stretch right up to the 760px centre column (20px breathing room) */
227
  width: max(190px, calc((100vw - 800px) / 2));
228
+ height: 100vh;
 
229
  background:
230
  radial-gradient(ellipse 90% 16% at 50% -3%, rgba(167,139,250,.10), transparent 60%),
231
  linear-gradient(180deg, #0B1126 0%, #0A0F1E 55%, #0C1226 100%);
232
  overflow: hidden;
233
+ /* no entrance animation: the BRIDGE_JS re-parent would restart it */
234
+ }
235
+ .lib-unit {
236
+ height: 950px;
237
+ display: flex; flex-direction: column; justify-content: space-between;
238
+ gap: 12px; padding: 26px 18px;
239
  }
240
  .library-left { left: 0; border-right: 1px solid var(--border); box-shadow: 18px 0 40px -20px rgba(0,0,0,.6); }
241
  .library-right { right: 0; border-left: 1px solid var(--border); box-shadow: -18px 0 40px -20px rgba(0,0,0,.6); }
 
886
  </style>
887
  <canvas id="particles"></canvas>
888
 
889
+ <!-- ── Library wings β€” page-height decor flanking the app (decorative).
890
+ BRIDGE_JS re-parents these onto <body>, sizes them to the full page
891
+ and clones .lib-unit as many times as needed to reach the bottom. ──── -->
892
  <div class="library library-left" aria-hidden="true">
893
+ <div class="lib-unit">
894
  <div class="lib-shelf">
895
  <i class="bk b2" style="--h:36px"></i><i class="bk b3" style="--h:50px"></i><i class="bk b1 slim" style="--h:56px"></i><i class="bk b6" style="--h:40px"></i>
896
  <i class="bk b1 lean" style="--h:34px"></i><i class="bk b6" style="--h:34px"></i><i class="bk b7 slim" style="--h:44px"></i><i class="bk b4" style="--h:46px"></i>
 
914
  <div class="lib-aisle"><div class="aisle-end"></div><span class="aisle-bot"></span></div>
915
  <div class="lib-tall">
916
  <div class="lib-shelf pad-r">
917
+ <i class="bk b2" style="--h:42px"></i><i class="bk b8" style="--h:52px"></i><i class="bk b5 slim" style="--h:32px"></i><i class="bk b7" style="--h:44px"></i>
918
+ <i class="bk b2" style="--h:54px"></i><i class="bk b3 wide" style="--h:52px"></i><i class="bk b7 slim" style="--h:50px"></i><i class="bk b3" style="--h:48px"></i>
919
+ <i class="bk b7" style="--h:56px"></i><i class="bk b5 lean" style="--h:34px"></i><i class="bk b3" style="--h:44px"></i><i class="bk b2" style="--h:28px"></i>
920
+ <i class="bk b1 slim" style="--h:28px"></i><i class="bk b3" style="--h:28px"></i><i class="bk b8 lean" style="--h:44px"></i><i class="bk b6" style="--h:44px"></i>
921
+ <i class="bk b2 slim" style="--h:56px"></i><i class="bk b6" style="--h:34px"></i><i class="bk b8" style="--h:40px"></i><i class="bk b2 lean" style="--h:48px"></i>
922
+ <i class="bk b5" style="--h:54px"></i><i class="bk b7" style="--h:48px"></i><i class="bk b6" style="--h:40px"></i><i class="bk b7" style="--h:54px"></i>
923
+ <i class="bk b1 lean" style="--h:34px"></i><i class="bk b3" style="--h:36px"></i><i class="bk b5" style="--h:56px"></i><i class="bk b1" style="--h:54px"></i>
924
  </div>
925
  <div class="lib-ladder"></div>
926
  <div class="mini-bot bot-climber"><div class="mini-head"><span class="mini-eye"></span><span class="mini-eye"></span></div><div class="mini-body"></div></div>
 
933
  <div class="lib-floor">
934
  <div class="mini-bot bot-walker"><div class="mini-head"><span class="mini-eye"></span><span class="mini-eye"></span></div><div class="mini-body"></div></div>
935
  </div>
936
+ </div>
937
  </div>
938
 
939
  <div class="library library-right" aria-hidden="true">
940
+ <div class="lib-unit">
941
  <div class="lib-shelf pad-r">
942
  <i class="bk b2" style="--h:44px"></i><i class="bk b8" style="--h:30px"></i><i class="bk b7 slim" style="--h:32px"></i><i class="bk b4" style="--h:34px"></i>
943
  <i class="bk b8 lean" style="--h:28px"></i><i class="bk b1 wide" style="--h:36px"></i><i class="bk b8 slim" style="--h:36px"></i><i class="bk b4 slim" style="--h:50px"></i>
 
961
  <div class="lib-aisle"><div class="aisle-end"></div><span class="aisle-bot" style="animation-delay:-4s"></span></div>
962
  <div class="lib-tall">
963
  <div class="lib-shelf pad-l" style="justify-content:flex-end">
964
+ <i class="bk b7 slim" style="--h:54px"></i><i class="bk b6 slim" style="--h:36px"></i><i class="bk b2 slim" style="--h:30px"></i><i class="bk b5 wide" style="--h:54px"></i>
965
+ <i class="bk b4" style="--h:54px"></i><i class="bk b3" style="--h:52px"></i><i class="bk b8" style="--h:28px"></i><i class="bk b6" style="--h:38px"></i>
966
+ <i class="bk b3 wide" style="--h:52px"></i><i class="bk b4 lean" style="--h:44px"></i><i class="bk b8" style="--h:28px"></i><i class="bk b1 wide" style="--h:36px"></i>
967
+ <i class="bk b3" style="--h:36px"></i><i class="bk b1" style="--h:40px"></i><i class="bk b4 slim lean" style="--h:38px"></i><i class="bk b6" style="--h:56px"></i>
968
+ <i class="bk b5" style="--h:28px"></i><i class="bk b7" style="--h:44px"></i><i class="bk b8" style="--h:50px"></i><i class="bk b6 slim lean" style="--h:34px"></i>
969
+ <i class="bk b1 wide" style="--h:56px"></i><i class="bk b4 slim" style="--h:48px"></i><i class="bk b8" style="--h:56px"></i><i class="bk b3" style="--h:48px"></i>
970
+ <i class="bk b5 lean" style="--h:50px"></i><i class="bk b2" style="--h:48px"></i><i class="bk b5 slim" style="--h:56px"></i><i class="bk b2 slim" style="--h:36px"></i>
971
  </div>
972
  <div class="mini-bot bot-fetcher"><div class="mini-head"><span class="mini-eye"></span><span class="mini-eye"></span></div><div class="mini-body"></div></div>
973
  </div>
 
982
  <div class="mini-head"><span class="mini-eye"></span><span class="mini-eye"></span></div><div class="mini-body"></div>
983
  </div>
984
  </div>
985
+ </div>
986
  </div>
987
 
988
  <div id="app">
 
1157
  <p class="modal-message" id="modal-message"></p>
1158
  <div class="modal-history hidden" id="modal-history"></div>
1159
  <div class="loading-msg hidden" id="modal-image-loading">
1160
+ <span class="spinner"></span> Preparing your reward…
1161
  </div>
1162
  <div class="hidden" id="modal-session-image">
1163
+ <div class="concept-image-title">Your Reward</div>
1164
+ <img id="session-image" alt="An encouraging illustration" />
1165
+ <p class="concept-image-caption">A little encouragement from your AI professor πŸŽ‰</p>
1166
  </div>
1167
  <button class="btn-primary" id="modal-close" style="width:100%;justify-content:center">
1168
  Study Again