youssefreda9 commited on
Commit
76c4caf
·
1 Parent(s): ebfec65

Implement all 19 remaining UI features: toolbar tools, find/replace, focus mode, error tabs, donut chart, version history, Arabic tools, tab animations, etc.

Browse files
Files changed (3) hide show
  1. src/css/components.css +493 -64
  2. src/index.html +39 -8
  3. src/js/format.js +479 -1
src/css/components.css CHANGED
@@ -299,11 +299,11 @@
299
  position: relative;
300
  }
301
 
302
- .editor-shell > *:first-child {
303
  border-radius: var(--radius-card) var(--radius-card) 0 0;
304
  }
305
 
306
- .editor-shell > *:last-child {
307
  border-radius: 0 0 var(--radius-card) var(--radius-card);
308
  }
309
 
@@ -317,11 +317,11 @@
317
  background: var(--color-surface);
318
  }
319
 
320
- .editor-toolbar > *:first-child {
321
  justify-self: start;
322
  }
323
 
324
- .editor-toolbar > *:last-child {
325
  justify-self: end;
326
  }
327
 
@@ -350,7 +350,7 @@
350
 
351
  .editor-tab:hover {
352
  color: var(--color-text-primary);
353
- background: rgba(255,255,255,0.06);
354
  }
355
 
356
  .editor-tab.active {
@@ -365,11 +365,13 @@
365
  align-items: center;
366
  gap: 5px;
367
  }
 
368
  .tab-icon {
369
  flex-shrink: 0;
370
  opacity: 0.8;
371
  vertical-align: middle;
372
  }
 
373
  .editor-tab.active .tab-icon {
374
  opacity: 1;
375
  }
@@ -378,6 +380,7 @@
378
  [data-tooltip] {
379
  position: relative;
380
  }
 
381
  [data-tooltip]::after {
382
  content: attr(data-tooltip);
383
  position: absolute;
@@ -395,11 +398,12 @@
395
  opacity: 0;
396
  visibility: hidden;
397
  transition: all 0.2s ease;
398
- box-shadow: 0 4px 12px rgba(0,0,0,0.2);
399
  border: 1px solid var(--color-border);
400
  z-index: 300;
401
  font-family: inherit;
402
  }
 
403
  [data-tooltip]:hover::after {
404
  opacity: 1;
405
  visibility: visible;
@@ -558,7 +562,7 @@
558
  background: var(--color-surface);
559
  border: 1px solid var(--color-border);
560
  border-radius: 8px;
561
- box-shadow: 0 8px 24px rgba(0,0,0,0.15);
562
  padding: 4px;
563
  z-index: 200;
564
  opacity: 0;
@@ -645,11 +649,15 @@
645
  }
646
 
647
  @media (min-width: 768px) {
648
- .editor-surface { min-height: 60vh; }
 
 
649
  }
650
 
651
  @media (min-width: 1024px) {
652
- .editor-surface { min-height: 500px; }
 
 
653
  }
654
 
655
  .editor-surface:focus {
@@ -701,15 +709,24 @@
701
  flex-shrink: 0;
702
  }
703
 
704
- .stat-dot--spelling { background: var(--color-error); }
705
- .stat-dot--grammar { background: var(--color-warning); }
706
- .stat-dot--punctuation { background: var(--color-success); }
 
 
 
 
 
 
 
 
707
 
708
  .editor-actions {
709
  display: flex;
710
  flex-wrap: wrap;
711
  gap: var(--spacing-sm);
712
  }
 
713
  .editor-actions .btn-ghost,
714
  .editor-actions .doc-dropdown .btn-ghost {
715
  width: 40px;
@@ -838,9 +855,17 @@
838
  gap: var(--spacing-sm);
839
  }
840
 
841
- .popover-type--spelling { color: var(--color-error); }
842
- .popover-type--grammar { color: var(--color-warning); }
843
- .popover-type--punctuation { color: var(--color-success); }
 
 
 
 
 
 
 
 
844
 
845
  .popover-original-word {
846
  font-size: var(--font-size-sm);
@@ -939,6 +964,7 @@
939
  cursor: pointer;
940
  transition: all var(--transition-fast);
941
  }
 
942
  .popover-dismiss:hover {
943
  color: var(--color-error);
944
  border-color: var(--color-error);
@@ -1021,8 +1047,15 @@
1021
  }
1022
 
1023
  @keyframes fadeIn {
1024
- from { opacity: 0; transform: translateY(4px); }
1025
- to { opacity: 1; transform: translateY(0); }
 
 
 
 
 
 
 
1026
  }
1027
 
1028
 
@@ -1455,8 +1488,9 @@ input[type="range"]::-webkit-slider-thumb {
1455
  .editor-layout.tools-mode {
1456
  grid-template-columns: 1fr !important;
1457
  }
1458
- .editor-layout.tools-mode > .docs-panel-desktop,
1459
- .editor-layout.tools-mode > .sidebar-panel {
 
1460
  display: none !important;
1461
  }
1462
 
@@ -1612,8 +1646,13 @@ input[type="range"]::-webkit-slider-thumb {
1612
  border: 1px solid var(--color-primary-subtle-border);
1613
  }
1614
 
1615
- .check-icon { color: var(--color-success); }
1616
- .muted-icon { color: var(--color-text-muted); }
 
 
 
 
 
1617
 
1618
  .demo-editor-surface {
1619
  background: var(--color-editor);
@@ -2034,7 +2073,8 @@ input[type="range"]::-webkit-slider-thumb {
2034
  transform: translateX(100%);
2035
  transition: transform var(--transition-slow), box-shadow var(--transition-slow);
2036
  box-shadow: none;
2037
- padding-top: 64px; /* below nav */
 
2038
  }
2039
 
2040
  .docs-sidebar.is-open {
@@ -2439,7 +2479,7 @@ input[type="range"]::-webkit-slider-thumb {
2439
  background: linear-gradient(135deg, var(--color-primary), var(--color-secondary));
2440
  cursor: pointer;
2441
  border: 3px solid var(--color-surface);
2442
- box-shadow: 0 2px 8px rgba(0,0,0,0.25);
2443
  transition: transform 0.15s ease;
2444
  }
2445
 
@@ -2454,7 +2494,7 @@ input[type="range"]::-webkit-slider-thumb {
2454
  background: linear-gradient(135deg, var(--color-primary), var(--color-secondary));
2455
  cursor: pointer;
2456
  border: 3px solid var(--color-surface);
2457
- box-shadow: 0 2px 8px rgba(0,0,0,0.25);
2458
  }
2459
 
2460
  /* ── Item 3: Empty States ── */
@@ -2644,6 +2684,7 @@ input[type="range"]::-webkit-slider-thumb {
2644
  font-weight: 700;
2645
  color: var(--color-text-muted);
2646
  }
 
2647
  .fmt-color-swatch--reset:hover {
2648
  border-color: var(--color-error);
2649
  color: var(--color-error);
@@ -2858,17 +2899,36 @@ input[type="range"]::-moz-range-thumb {
2858
  animation: toast-out 0.25s ease forwards;
2859
  }
2860
 
2861
- .toast--success { border-color: rgba(34, 197, 94, 0.4); }
2862
- .toast--error { border-color: rgba(239, 68, 68, 0.4); }
 
 
 
 
 
2863
 
2864
  @keyframes toast-in {
2865
- from { opacity: 0; transform: translateY(16px); }
2866
- to { opacity: 1; transform: translateY(0); }
 
 
 
 
 
 
 
2867
  }
2868
 
2869
  @keyframes toast-out {
2870
- from { opacity: 1; transform: translateY(0); }
2871
- to { opacity: 0; transform: translateY(16px); }
 
 
 
 
 
 
 
2872
  }
2873
 
2874
  /* ── Scroll to Top ── */
@@ -2907,7 +2967,7 @@ input[type="range"]::-moz-range-thumb {
2907
 
2908
  /* ── Loading Skeleton ── */
2909
  .skeleton {
2910
- background: linear-gradient(90deg, var(--color-border) 25%, rgba(255,255,255,0.08) 50%, var(--color-border) 75%);
2911
  background-size: 200% 100%;
2912
  animation: skeleton-shimmer 1.5s ease infinite;
2913
  border-radius: 8px;
@@ -2924,8 +2984,13 @@ input[type="range"]::-moz-range-thumb {
2924
  }
2925
 
2926
  @keyframes skeleton-shimmer {
2927
- 0% { background-position: 200% 0; }
2928
- 100% { background-position: -200% 0; }
 
 
 
 
 
2929
  }
2930
 
2931
  /* ── Word Count Goal ── */
@@ -2984,8 +3049,15 @@ input[type="range"]::-moz-range-thumb {
2984
  }
2985
 
2986
  @keyframes punc-blink {
2987
- 0%, 100% { border-left-color: var(--highlight-punctuation-border); }
2988
- 50% { border-left-color: transparent; }
 
 
 
 
 
 
 
2989
  }
2990
 
2991
  /* ── Fix #6: Document title truncation ── */
@@ -3001,11 +3073,12 @@ input[type="range"]::-moz-range-thumb {
3001
  /* ── Fix #8: Editor placeholder alignment (Removed hack) ── */
3002
 
3003
  /* ── Fix #11: Pricing cards equal heights ── */
3004
- #page-pricing .grid > div {
3005
  display: flex;
3006
  flex-direction: column;
3007
  }
3008
- #page-pricing .grid > div > ul {
 
3009
  flex: 1;
3010
  }
3011
 
@@ -3014,15 +3087,18 @@ input[type="range"]::-moz-range-thumb {
3014
  cursor: default;
3015
  transition: transform 0.3s ease, filter 0.3s ease, box-shadow 0.3s ease;
3016
  }
 
3017
  .window-dots:hover .dot--red {
3018
  transform: scale(1.25);
3019
  box-shadow: 0 0 8px rgba(239, 68, 68, 0.5);
3020
  }
 
3021
  .window-dots:hover .dot--yellow {
3022
  transform: scale(1.25);
3023
  box-shadow: 0 0 8px rgba(234, 179, 8, 0.5);
3024
  transition-delay: 0.05s;
3025
  }
 
3026
  .window-dots:hover .dot--green {
3027
  transform: scale(1.25);
3028
  box-shadow: 0 0 8px rgba(34, 197, 94, 0.5);
@@ -3033,9 +3109,11 @@ input[type="range"]::-moz-range-thumb {
3033
  .sidebar-desktop.collapsed {
3034
  display: none !important;
3035
  }
 
3036
  .editor-layout.editor-fullscreen {
3037
  grid-template-columns: 1fr !important;
3038
  }
 
3039
  .editor-layout.editor-fullscreen .sidebar-panel.sidebar-desktop,
3040
  .editor-layout.editor-fullscreen .docs-panel,
3041
  .editor-layout.editor-fullscreen .docs-panel-desktop {
@@ -3047,38 +3125,47 @@ input[type="range"]::-moz-range-thumb {
3047
  color: #3A424E;
3048
  background: rgba(26, 29, 33, 0.04);
3049
  }
 
3050
  [data-theme="light"] .fmt-btn:hover {
3051
  background: rgba(26, 29, 33, 0.1);
3052
  color: #1A1D21;
3053
  }
 
3054
  [data-theme="light"] .fmt-btn.fmt-active {
3055
  background: rgba(43, 108, 184, 0.14);
3056
  color: #2B6CB8;
3057
  }
 
3058
  [data-theme="light"] .format-toolbar {
3059
  background: #FAF9F6;
3060
  border-bottom-color: rgba(26, 29, 33, 0.12);
3061
  }
 
3062
  [data-theme="light"] .surface-card {
3063
  box-shadow: 0 2px 8px rgba(26, 29, 33, 0.06);
3064
  border: 1px solid rgba(26, 29, 33, 0.08);
3065
  }
 
3066
  [data-theme="light"] .editor-tab {
3067
  color: #5A6472;
3068
  }
 
3069
  [data-theme="light"] .editor-tab:hover {
3070
  color: #1A1D21;
3071
  background: rgba(26, 29, 33, 0.06);
3072
  }
 
3073
  [data-theme="light"] .editor-tab.active {
3074
  color: #fff !important;
3075
  background: linear-gradient(135deg, #4A8FD4, #7C6BC4);
3076
  box-shadow: 0 2px 8px rgba(74, 143, 212, 0.25);
3077
  }
 
3078
  [data-theme="light"] .stat-item {
3079
  color: #3A424E;
3080
  opacity: 1;
3081
  }
 
3082
  [data-theme="light"] .scroll-top-btn {
3083
  box-shadow: 0 4px 16px rgba(26, 29, 33, 0.15);
3084
  }
@@ -3109,7 +3196,7 @@ input[type="range"]::-moz-range-thumb {
3109
  #autocomplete-dropdown {
3110
  position: fixed;
3111
  background: var(--color-surface, #1a1d21);
3112
- border: 1px solid var(--color-border, rgba(255,255,255,0.1));
3113
  border-radius: 10px;
3114
  box-shadow:
3115
  0 4px 24px rgba(0, 0, 0, 0.25),
@@ -3126,8 +3213,15 @@ input[type="range"]::-moz-range-thumb {
3126
  }
3127
 
3128
  @keyframes ac-dropdown-in {
3129
- from { opacity: 0; transform: translateY(-4px); }
3130
- to { opacity: 1; transform: translateY(0); }
 
 
 
 
 
 
 
3131
  }
3132
 
3133
  /* ── Dropdown Items ── */
@@ -3147,8 +3241,8 @@ input[type="range"]::-moz-range-thumb {
3147
  .ac-dropdown-item:hover,
3148
  .ac-dropdown-item.ac-selected {
3149
  background: linear-gradient(135deg,
3150
- rgba(107, 163, 224, 0.15),
3151
- rgba(160, 131, 237, 0.12));
3152
  color: var(--color-primary, #6ba3e0);
3153
  }
3154
 
@@ -3160,9 +3254,11 @@ input[type="range"]::-moz-range-thumb {
3160
  #autocomplete-dropdown::-webkit-scrollbar {
3161
  width: 4px;
3162
  }
 
3163
  #autocomplete-dropdown::-webkit-scrollbar-track {
3164
  background: transparent;
3165
  }
 
3166
  #autocomplete-dropdown::-webkit-scrollbar-thumb {
3167
  background: rgba(255, 255, 255, 0.12);
3168
  border-radius: 2px;
@@ -3194,6 +3290,7 @@ input[type="range"]::-moz-range-thumb {
3194
 
3195
  /* ── Print: hide autocomplete ── */
3196
  @media print {
 
3197
  #autocomplete-ghost,
3198
  #autocomplete-dropdown {
3199
  display: none !important;
@@ -3210,7 +3307,10 @@ input[type="range"]::-moz-range-thumb {
3210
  justify-content: center;
3211
  padding: 1rem;
3212
  }
3213
- .quran-modal.is-hidden { display: none; }
 
 
 
3214
 
3215
  /* Quran applied text — protected from analysis */
3216
  .quran-applied {
@@ -3222,6 +3322,7 @@ input[type="range"]::-moz-range-thumb {
3222
  cursor: default;
3223
  user-select: text;
3224
  }
 
3225
  .quran-applied .spelling-error,
3226
  .quran-applied .grammar-error,
3227
  .quran-applied .punctuation-suggestion {
@@ -3230,6 +3331,7 @@ input[type="range"]::-moz-range-thumb {
3230
  background: none !important;
3231
  cursor: default !important;
3232
  }
 
3233
  .quran-ref-inline {
3234
  font-size: 0.72em;
3235
  opacity: 0.6;
@@ -3261,8 +3363,15 @@ input[type="range"]::-moz-range-thumb {
3261
  }
3262
 
3263
  @keyframes quranModalIn {
3264
- from { opacity: 0; transform: scale(0.95) translateY(10px); }
3265
- to { opacity: 1; transform: scale(1) translateY(0); }
 
 
 
 
 
 
 
3266
  }
3267
 
3268
  .quran-result-card {
@@ -3302,11 +3411,15 @@ input[type="range"]::-moz-range-thumb {
3302
  transition: all 0.2s;
3303
  white-space: nowrap;
3304
  }
 
3305
  .quran-apply-btn:hover {
3306
  transform: scale(1.05);
3307
  box-shadow: 0 4px 12px rgba(6, 182, 212, 0.35);
3308
  }
3309
- .quran-apply-btn.is-hidden { display: none; }
 
 
 
3310
 
3311
  .quran-copy-btn {
3312
  padding: 6px 10px;
@@ -3319,11 +3432,15 @@ input[type="range"]::-moz-range-thumb {
3319
  transition: all 0.2s;
3320
  line-height: 1;
3321
  }
 
3322
  .quran-copy-btn:hover {
3323
  background: rgba(6, 182, 212, 0.2);
3324
  transform: scale(1.05);
3325
  }
3326
- .quran-copy-btn.is-hidden { display: none; }
 
 
 
3327
 
3328
  .quran-lang-select {
3329
  padding: 6px 12px;
@@ -3336,9 +3453,11 @@ input[type="range"]::-moz-range-thumb {
3336
  cursor: pointer;
3337
  transition: border-color 0.2s;
3338
  }
 
3339
  .quran-lang-select:hover {
3340
  border-color: #06b6d4;
3341
  }
 
3342
  .quran-lang-select:focus {
3343
  outline: none;
3344
  border-color: #06b6d4;
@@ -3351,6 +3470,7 @@ input[type="range"]::-moz-range-thumb {
3351
  border-radius: 1.25rem;
3352
  padding: 1.25rem;
3353
  }
 
3354
  .quran-uthmani {
3355
  font-size: 20px;
3356
  }
@@ -3370,6 +3490,7 @@ input[type="range"]::-moz-range-thumb {
3370
  .card-hover {
3371
  transition: transform var(--transition-base), box-shadow var(--transition-base);
3372
  }
 
3373
  .card-hover:hover {
3374
  transform: translateY(-3px);
3375
  box-shadow: var(--shadow-glow);
@@ -3385,6 +3506,7 @@ input[type="range"]::-moz-range-thumb {
3385
  text-align: center;
3386
  gap: 0.75rem;
3387
  }
 
3388
  .empty-state__icon {
3389
  width: 48px;
3390
  height: 48px;
@@ -3396,11 +3518,13 @@ input[type="range"]::-moz-range-thumb {
3396
  color: var(--color-primary);
3397
  margin-bottom: 0.25rem;
3398
  }
 
3399
  .empty-state__title {
3400
  font-size: var(--font-size-body);
3401
  font-weight: var(--font-weight-semibold);
3402
  color: var(--color-text-primary);
3403
  }
 
3404
  .empty-state__subtitle {
3405
  font-size: var(--font-size-caption);
3406
  color: var(--color-text-secondary);
@@ -3419,6 +3543,7 @@ input[type="range"]::-moz-range-thumb {
3419
  justify-content: center;
3420
  animation: fadeIn 0.15s ease-out;
3421
  }
 
3422
  .confirm-dialog {
3423
  background: var(--color-surface);
3424
  border: 1px solid var(--color-border-strong);
@@ -3430,22 +3555,26 @@ input[type="range"]::-moz-range-thumb {
3430
  text-align: center;
3431
  animation: modalSlideUp 0.25s ease-out;
3432
  }
 
3433
  .confirm-dialog__title {
3434
  font-size: 1.125rem;
3435
  font-weight: var(--font-weight-bold);
3436
  margin-bottom: 0.5rem;
3437
  }
 
3438
  .confirm-dialog__message {
3439
  font-size: var(--font-size-caption);
3440
  color: var(--color-text-secondary);
3441
  margin-bottom: 1.5rem;
3442
  line-height: 1.7;
3443
  }
 
3444
  .confirm-dialog__actions {
3445
  display: flex;
3446
  gap: 0.75rem;
3447
  justify-content: center;
3448
  }
 
3449
  .confirm-dialog__btn {
3450
  padding: 0.6rem 1.5rem;
3451
  border-radius: var(--radius-md);
@@ -3458,23 +3587,34 @@ input[type="range"]::-moz-range-thumb {
3458
  transition: all var(--transition-fast);
3459
  font-family: inherit;
3460
  }
 
3461
  .confirm-dialog__btn:hover {
3462
  background: var(--color-border);
3463
  }
 
3464
  .confirm-dialog__btn--danger {
3465
  background: var(--color-error);
3466
  color: white;
3467
  border-color: transparent;
3468
  }
 
3469
  .confirm-dialog__btn--danger:hover {
3470
  opacity: 0.9;
3471
  }
3472
 
3473
  /* ── Modal entrance animation ── */
3474
  @keyframes modalSlideUp {
3475
- from { opacity: 0; transform: translateY(16px) scale(0.97); }
3476
- to { opacity: 1; transform: translateY(0) scale(1); }
 
 
 
 
 
 
 
3477
  }
 
3478
  .quran-modal-panel,
3479
  .auth-gate-panel,
3480
  .confirm-dialog {
@@ -3482,14 +3622,23 @@ input[type="range"]::-moz-range-thumb {
3482
  }
3483
 
3484
  /* ── Toast with icons ── */
3485
- .toast--success::before { content: '✓ '; }
3486
- .toast--error::before { content: ' '; }
3487
- .toast--warning::before { content: '⚠ '; }
 
 
 
 
 
 
 
 
3488
 
3489
  /* ── Pricing card glow (active plan) ── */
3490
  .pricing-glow {
3491
  position: relative;
3492
  }
 
3493
  .pricing-glow::before {
3494
  content: '';
3495
  position: absolute;
@@ -3501,24 +3650,37 @@ input[type="range"]::-moz-range-thumb {
3501
  filter: blur(12px);
3502
  animation: pricingPulse 3s ease-in-out infinite;
3503
  }
 
3504
  @keyframes pricingPulse {
3505
- 0%, 100% { opacity: 0.3; }
3506
- 50% { opacity: 0.6; }
 
 
 
 
 
 
 
3507
  }
3508
 
3509
  /* ── Beta banner shimmer ── */
3510
  @keyframes betaShimmer {
3511
- 0% { background-position: -200% center; }
3512
- 100% { background-position: 200% center; }
 
 
 
 
 
3513
  }
 
3514
  .beta-shimmer {
3515
  background-size: 200% auto;
3516
  background-image: linear-gradient(90deg,
3517
- transparent 0%,
3518
- rgba(107, 163, 224, 0.06) 40%,
3519
- rgba(165, 148, 232, 0.08) 60%,
3520
- transparent 100%
3521
- );
3522
  animation: betaShimmer 4s linear infinite;
3523
  }
3524
 
@@ -3556,19 +3718,29 @@ select:focus-visible,
3556
  border-radius: 12px;
3557
  background: var(--color-surface-elevated);
3558
  border: 1px solid var(--color-border-strong);
3559
- box-shadow: 0 8px 32px rgba(0,0,0,0.18), 0 0 0 1px rgba(255,255,255,0.05);
3560
  backdrop-filter: blur(16px);
3561
  transform: translateX(-50%);
3562
  animation: selbar-in 0.2s ease;
3563
  pointer-events: auto;
3564
  }
 
3565
  .selection-toolbar.is-hidden {
3566
  display: none;
3567
  }
 
3568
  @keyframes selbar-in {
3569
- from { opacity: 0; transform: translateX(-50%) translateY(6px); }
3570
- to { opacity: 1; transform: translateX(-50%) translateY(0); }
 
 
 
 
 
 
 
3571
  }
 
3572
  .sel-tool-btn {
3573
  display: inline-flex;
3574
  align-items: center;
@@ -3585,10 +3757,12 @@ select:focus-visible,
3585
  transition: all 0.15s ease;
3586
  white-space: nowrap;
3587
  }
 
3588
  .sel-tool-btn:hover {
3589
  background: var(--color-surface);
3590
  color: var(--color-text-primary);
3591
  }
 
3592
  .sel-tool-sep {
3593
  width: 1px;
3594
  height: 20px;
@@ -3606,6 +3780,7 @@ select:focus-visible,
3606
  padding: 3px;
3607
  overflow: hidden;
3608
  }
 
3609
  .summary-source-btn {
3610
  flex: 1;
3611
  padding: 8px 16px;
@@ -3619,9 +3794,11 @@ select:focus-visible,
3619
  cursor: pointer;
3620
  transition: all 0.2s ease;
3621
  }
 
3622
  .summary-source-btn:hover {
3623
  color: var(--color-text-primary);
3624
  }
 
3625
  .summary-source-btn.active {
3626
  background: linear-gradient(135deg, var(--color-primary), var(--color-secondary));
3627
  color: var(--color-text-inverse);
@@ -3633,6 +3810,258 @@ select:focus-visible,
3633
  margin-left: 4px;
3634
  opacity: 0.7;
3635
  }
 
3636
  .editor-tab.active svg {
3637
  opacity: 1;
3638
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
299
  position: relative;
300
  }
301
 
302
+ .editor-shell>*:first-child {
303
  border-radius: var(--radius-card) var(--radius-card) 0 0;
304
  }
305
 
306
+ .editor-shell>*:last-child {
307
  border-radius: 0 0 var(--radius-card) var(--radius-card);
308
  }
309
 
 
317
  background: var(--color-surface);
318
  }
319
 
320
+ .editor-toolbar>*:first-child {
321
  justify-self: start;
322
  }
323
 
324
+ .editor-toolbar>*:last-child {
325
  justify-self: end;
326
  }
327
 
 
350
 
351
  .editor-tab:hover {
352
  color: var(--color-text-primary);
353
+ background: rgba(255, 255, 255, 0.06);
354
  }
355
 
356
  .editor-tab.active {
 
365
  align-items: center;
366
  gap: 5px;
367
  }
368
+
369
  .tab-icon {
370
  flex-shrink: 0;
371
  opacity: 0.8;
372
  vertical-align: middle;
373
  }
374
+
375
  .editor-tab.active .tab-icon {
376
  opacity: 1;
377
  }
 
380
  [data-tooltip] {
381
  position: relative;
382
  }
383
+
384
  [data-tooltip]::after {
385
  content: attr(data-tooltip);
386
  position: absolute;
 
398
  opacity: 0;
399
  visibility: hidden;
400
  transition: all 0.2s ease;
401
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
402
  border: 1px solid var(--color-border);
403
  z-index: 300;
404
  font-family: inherit;
405
  }
406
+
407
  [data-tooltip]:hover::after {
408
  opacity: 1;
409
  visibility: visible;
 
562
  background: var(--color-surface);
563
  border: 1px solid var(--color-border);
564
  border-radius: 8px;
565
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
566
  padding: 4px;
567
  z-index: 200;
568
  opacity: 0;
 
649
  }
650
 
651
  @media (min-width: 768px) {
652
+ .editor-surface {
653
+ min-height: 60vh;
654
+ }
655
  }
656
 
657
  @media (min-width: 1024px) {
658
+ .editor-surface {
659
+ min-height: 500px;
660
+ }
661
  }
662
 
663
  .editor-surface:focus {
 
709
  flex-shrink: 0;
710
  }
711
 
712
+ .stat-dot--spelling {
713
+ background: var(--color-error);
714
+ }
715
+
716
+ .stat-dot--grammar {
717
+ background: var(--color-warning);
718
+ }
719
+
720
+ .stat-dot--punctuation {
721
+ background: var(--color-success);
722
+ }
723
 
724
  .editor-actions {
725
  display: flex;
726
  flex-wrap: wrap;
727
  gap: var(--spacing-sm);
728
  }
729
+
730
  .editor-actions .btn-ghost,
731
  .editor-actions .doc-dropdown .btn-ghost {
732
  width: 40px;
 
855
  gap: var(--spacing-sm);
856
  }
857
 
858
+ .popover-type--spelling {
859
+ color: var(--color-error);
860
+ }
861
+
862
+ .popover-type--grammar {
863
+ color: var(--color-warning);
864
+ }
865
+
866
+ .popover-type--punctuation {
867
+ color: var(--color-success);
868
+ }
869
 
870
  .popover-original-word {
871
  font-size: var(--font-size-sm);
 
964
  cursor: pointer;
965
  transition: all var(--transition-fast);
966
  }
967
+
968
  .popover-dismiss:hover {
969
  color: var(--color-error);
970
  border-color: var(--color-error);
 
1047
  }
1048
 
1049
  @keyframes fadeIn {
1050
+ from {
1051
+ opacity: 0;
1052
+ transform: translateY(4px);
1053
+ }
1054
+
1055
+ to {
1056
+ opacity: 1;
1057
+ transform: translateY(0);
1058
+ }
1059
  }
1060
 
1061
 
 
1488
  .editor-layout.tools-mode {
1489
  grid-template-columns: 1fr !important;
1490
  }
1491
+
1492
+ .editor-layout.tools-mode>.docs-panel-desktop,
1493
+ .editor-layout.tools-mode>.sidebar-panel {
1494
  display: none !important;
1495
  }
1496
 
 
1646
  border: 1px solid var(--color-primary-subtle-border);
1647
  }
1648
 
1649
+ .check-icon {
1650
+ color: var(--color-success);
1651
+ }
1652
+
1653
+ .muted-icon {
1654
+ color: var(--color-text-muted);
1655
+ }
1656
 
1657
  .demo-editor-surface {
1658
  background: var(--color-editor);
 
2073
  transform: translateX(100%);
2074
  transition: transform var(--transition-slow), box-shadow var(--transition-slow);
2075
  box-shadow: none;
2076
+ padding-top: 64px;
2077
+ /* below nav */
2078
  }
2079
 
2080
  .docs-sidebar.is-open {
 
2479
  background: linear-gradient(135deg, var(--color-primary), var(--color-secondary));
2480
  cursor: pointer;
2481
  border: 3px solid var(--color-surface);
2482
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
2483
  transition: transform 0.15s ease;
2484
  }
2485
 
 
2494
  background: linear-gradient(135deg, var(--color-primary), var(--color-secondary));
2495
  cursor: pointer;
2496
  border: 3px solid var(--color-surface);
2497
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
2498
  }
2499
 
2500
  /* ── Item 3: Empty States ── */
 
2684
  font-weight: 700;
2685
  color: var(--color-text-muted);
2686
  }
2687
+
2688
  .fmt-color-swatch--reset:hover {
2689
  border-color: var(--color-error);
2690
  color: var(--color-error);
 
2899
  animation: toast-out 0.25s ease forwards;
2900
  }
2901
 
2902
+ .toast--success {
2903
+ border-color: rgba(34, 197, 94, 0.4);
2904
+ }
2905
+
2906
+ .toast--error {
2907
+ border-color: rgba(239, 68, 68, 0.4);
2908
+ }
2909
 
2910
  @keyframes toast-in {
2911
+ from {
2912
+ opacity: 0;
2913
+ transform: translateY(16px);
2914
+ }
2915
+
2916
+ to {
2917
+ opacity: 1;
2918
+ transform: translateY(0);
2919
+ }
2920
  }
2921
 
2922
  @keyframes toast-out {
2923
+ from {
2924
+ opacity: 1;
2925
+ transform: translateY(0);
2926
+ }
2927
+
2928
+ to {
2929
+ opacity: 0;
2930
+ transform: translateY(16px);
2931
+ }
2932
  }
2933
 
2934
  /* ── Scroll to Top ── */
 
2967
 
2968
  /* ── Loading Skeleton ── */
2969
  .skeleton {
2970
+ background: linear-gradient(90deg, var(--color-border) 25%, rgba(255, 255, 255, 0.08) 50%, var(--color-border) 75%);
2971
  background-size: 200% 100%;
2972
  animation: skeleton-shimmer 1.5s ease infinite;
2973
  border-radius: 8px;
 
2984
  }
2985
 
2986
  @keyframes skeleton-shimmer {
2987
+ 0% {
2988
+ background-position: 200% 0;
2989
+ }
2990
+
2991
+ 100% {
2992
+ background-position: -200% 0;
2993
+ }
2994
  }
2995
 
2996
  /* ── Word Count Goal ── */
 
3049
  }
3050
 
3051
  @keyframes punc-blink {
3052
+
3053
+ 0%,
3054
+ 100% {
3055
+ border-left-color: var(--highlight-punctuation-border);
3056
+ }
3057
+
3058
+ 50% {
3059
+ border-left-color: transparent;
3060
+ }
3061
  }
3062
 
3063
  /* ── Fix #6: Document title truncation ── */
 
3073
  /* ── Fix #8: Editor placeholder alignment (Removed hack) ── */
3074
 
3075
  /* ── Fix #11: Pricing cards equal heights ── */
3076
+ #page-pricing .grid>div {
3077
  display: flex;
3078
  flex-direction: column;
3079
  }
3080
+
3081
+ #page-pricing .grid>div>ul {
3082
  flex: 1;
3083
  }
3084
 
 
3087
  cursor: default;
3088
  transition: transform 0.3s ease, filter 0.3s ease, box-shadow 0.3s ease;
3089
  }
3090
+
3091
  .window-dots:hover .dot--red {
3092
  transform: scale(1.25);
3093
  box-shadow: 0 0 8px rgba(239, 68, 68, 0.5);
3094
  }
3095
+
3096
  .window-dots:hover .dot--yellow {
3097
  transform: scale(1.25);
3098
  box-shadow: 0 0 8px rgba(234, 179, 8, 0.5);
3099
  transition-delay: 0.05s;
3100
  }
3101
+
3102
  .window-dots:hover .dot--green {
3103
  transform: scale(1.25);
3104
  box-shadow: 0 0 8px rgba(34, 197, 94, 0.5);
 
3109
  .sidebar-desktop.collapsed {
3110
  display: none !important;
3111
  }
3112
+
3113
  .editor-layout.editor-fullscreen {
3114
  grid-template-columns: 1fr !important;
3115
  }
3116
+
3117
  .editor-layout.editor-fullscreen .sidebar-panel.sidebar-desktop,
3118
  .editor-layout.editor-fullscreen .docs-panel,
3119
  .editor-layout.editor-fullscreen .docs-panel-desktop {
 
3125
  color: #3A424E;
3126
  background: rgba(26, 29, 33, 0.04);
3127
  }
3128
+
3129
  [data-theme="light"] .fmt-btn:hover {
3130
  background: rgba(26, 29, 33, 0.1);
3131
  color: #1A1D21;
3132
  }
3133
+
3134
  [data-theme="light"] .fmt-btn.fmt-active {
3135
  background: rgba(43, 108, 184, 0.14);
3136
  color: #2B6CB8;
3137
  }
3138
+
3139
  [data-theme="light"] .format-toolbar {
3140
  background: #FAF9F6;
3141
  border-bottom-color: rgba(26, 29, 33, 0.12);
3142
  }
3143
+
3144
  [data-theme="light"] .surface-card {
3145
  box-shadow: 0 2px 8px rgba(26, 29, 33, 0.06);
3146
  border: 1px solid rgba(26, 29, 33, 0.08);
3147
  }
3148
+
3149
  [data-theme="light"] .editor-tab {
3150
  color: #5A6472;
3151
  }
3152
+
3153
  [data-theme="light"] .editor-tab:hover {
3154
  color: #1A1D21;
3155
  background: rgba(26, 29, 33, 0.06);
3156
  }
3157
+
3158
  [data-theme="light"] .editor-tab.active {
3159
  color: #fff !important;
3160
  background: linear-gradient(135deg, #4A8FD4, #7C6BC4);
3161
  box-shadow: 0 2px 8px rgba(74, 143, 212, 0.25);
3162
  }
3163
+
3164
  [data-theme="light"] .stat-item {
3165
  color: #3A424E;
3166
  opacity: 1;
3167
  }
3168
+
3169
  [data-theme="light"] .scroll-top-btn {
3170
  box-shadow: 0 4px 16px rgba(26, 29, 33, 0.15);
3171
  }
 
3196
  #autocomplete-dropdown {
3197
  position: fixed;
3198
  background: var(--color-surface, #1a1d21);
3199
+ border: 1px solid var(--color-border, rgba(255, 255, 255, 0.1));
3200
  border-radius: 10px;
3201
  box-shadow:
3202
  0 4px 24px rgba(0, 0, 0, 0.25),
 
3213
  }
3214
 
3215
  @keyframes ac-dropdown-in {
3216
+ from {
3217
+ opacity: 0;
3218
+ transform: translateY(-4px);
3219
+ }
3220
+
3221
+ to {
3222
+ opacity: 1;
3223
+ transform: translateY(0);
3224
+ }
3225
  }
3226
 
3227
  /* ── Dropdown Items ── */
 
3241
  .ac-dropdown-item:hover,
3242
  .ac-dropdown-item.ac-selected {
3243
  background: linear-gradient(135deg,
3244
+ rgba(107, 163, 224, 0.15),
3245
+ rgba(160, 131, 237, 0.12));
3246
  color: var(--color-primary, #6ba3e0);
3247
  }
3248
 
 
3254
  #autocomplete-dropdown::-webkit-scrollbar {
3255
  width: 4px;
3256
  }
3257
+
3258
  #autocomplete-dropdown::-webkit-scrollbar-track {
3259
  background: transparent;
3260
  }
3261
+
3262
  #autocomplete-dropdown::-webkit-scrollbar-thumb {
3263
  background: rgba(255, 255, 255, 0.12);
3264
  border-radius: 2px;
 
3290
 
3291
  /* ── Print: hide autocomplete ── */
3292
  @media print {
3293
+
3294
  #autocomplete-ghost,
3295
  #autocomplete-dropdown {
3296
  display: none !important;
 
3307
  justify-content: center;
3308
  padding: 1rem;
3309
  }
3310
+
3311
+ .quran-modal.is-hidden {
3312
+ display: none;
3313
+ }
3314
 
3315
  /* Quran applied text — protected from analysis */
3316
  .quran-applied {
 
3322
  cursor: default;
3323
  user-select: text;
3324
  }
3325
+
3326
  .quran-applied .spelling-error,
3327
  .quran-applied .grammar-error,
3328
  .quran-applied .punctuation-suggestion {
 
3331
  background: none !important;
3332
  cursor: default !important;
3333
  }
3334
+
3335
  .quran-ref-inline {
3336
  font-size: 0.72em;
3337
  opacity: 0.6;
 
3363
  }
3364
 
3365
  @keyframes quranModalIn {
3366
+ from {
3367
+ opacity: 0;
3368
+ transform: scale(0.95) translateY(10px);
3369
+ }
3370
+
3371
+ to {
3372
+ opacity: 1;
3373
+ transform: scale(1) translateY(0);
3374
+ }
3375
  }
3376
 
3377
  .quran-result-card {
 
3411
  transition: all 0.2s;
3412
  white-space: nowrap;
3413
  }
3414
+
3415
  .quran-apply-btn:hover {
3416
  transform: scale(1.05);
3417
  box-shadow: 0 4px 12px rgba(6, 182, 212, 0.35);
3418
  }
3419
+
3420
+ .quran-apply-btn.is-hidden {
3421
+ display: none;
3422
+ }
3423
 
3424
  .quran-copy-btn {
3425
  padding: 6px 10px;
 
3432
  transition: all 0.2s;
3433
  line-height: 1;
3434
  }
3435
+
3436
  .quran-copy-btn:hover {
3437
  background: rgba(6, 182, 212, 0.2);
3438
  transform: scale(1.05);
3439
  }
3440
+
3441
+ .quran-copy-btn.is-hidden {
3442
+ display: none;
3443
+ }
3444
 
3445
  .quran-lang-select {
3446
  padding: 6px 12px;
 
3453
  cursor: pointer;
3454
  transition: border-color 0.2s;
3455
  }
3456
+
3457
  .quran-lang-select:hover {
3458
  border-color: #06b6d4;
3459
  }
3460
+
3461
  .quran-lang-select:focus {
3462
  outline: none;
3463
  border-color: #06b6d4;
 
3470
  border-radius: 1.25rem;
3471
  padding: 1.25rem;
3472
  }
3473
+
3474
  .quran-uthmani {
3475
  font-size: 20px;
3476
  }
 
3490
  .card-hover {
3491
  transition: transform var(--transition-base), box-shadow var(--transition-base);
3492
  }
3493
+
3494
  .card-hover:hover {
3495
  transform: translateY(-3px);
3496
  box-shadow: var(--shadow-glow);
 
3506
  text-align: center;
3507
  gap: 0.75rem;
3508
  }
3509
+
3510
  .empty-state__icon {
3511
  width: 48px;
3512
  height: 48px;
 
3518
  color: var(--color-primary);
3519
  margin-bottom: 0.25rem;
3520
  }
3521
+
3522
  .empty-state__title {
3523
  font-size: var(--font-size-body);
3524
  font-weight: var(--font-weight-semibold);
3525
  color: var(--color-text-primary);
3526
  }
3527
+
3528
  .empty-state__subtitle {
3529
  font-size: var(--font-size-caption);
3530
  color: var(--color-text-secondary);
 
3543
  justify-content: center;
3544
  animation: fadeIn 0.15s ease-out;
3545
  }
3546
+
3547
  .confirm-dialog {
3548
  background: var(--color-surface);
3549
  border: 1px solid var(--color-border-strong);
 
3555
  text-align: center;
3556
  animation: modalSlideUp 0.25s ease-out;
3557
  }
3558
+
3559
  .confirm-dialog__title {
3560
  font-size: 1.125rem;
3561
  font-weight: var(--font-weight-bold);
3562
  margin-bottom: 0.5rem;
3563
  }
3564
+
3565
  .confirm-dialog__message {
3566
  font-size: var(--font-size-caption);
3567
  color: var(--color-text-secondary);
3568
  margin-bottom: 1.5rem;
3569
  line-height: 1.7;
3570
  }
3571
+
3572
  .confirm-dialog__actions {
3573
  display: flex;
3574
  gap: 0.75rem;
3575
  justify-content: center;
3576
  }
3577
+
3578
  .confirm-dialog__btn {
3579
  padding: 0.6rem 1.5rem;
3580
  border-radius: var(--radius-md);
 
3587
  transition: all var(--transition-fast);
3588
  font-family: inherit;
3589
  }
3590
+
3591
  .confirm-dialog__btn:hover {
3592
  background: var(--color-border);
3593
  }
3594
+
3595
  .confirm-dialog__btn--danger {
3596
  background: var(--color-error);
3597
  color: white;
3598
  border-color: transparent;
3599
  }
3600
+
3601
  .confirm-dialog__btn--danger:hover {
3602
  opacity: 0.9;
3603
  }
3604
 
3605
  /* ── Modal entrance animation ── */
3606
  @keyframes modalSlideUp {
3607
+ from {
3608
+ opacity: 0;
3609
+ transform: translateY(16px) scale(0.97);
3610
+ }
3611
+
3612
+ to {
3613
+ opacity: 1;
3614
+ transform: translateY(0) scale(1);
3615
+ }
3616
  }
3617
+
3618
  .quran-modal-panel,
3619
  .auth-gate-panel,
3620
  .confirm-dialog {
 
3622
  }
3623
 
3624
  /* ── Toast with icons ── */
3625
+ .toast--success::before {
3626
+ content: ' ';
3627
+ }
3628
+
3629
+ .toast--error::before {
3630
+ content: '✕ ';
3631
+ }
3632
+
3633
+ .toast--warning::before {
3634
+ content: '⚠ ';
3635
+ }
3636
 
3637
  /* ── Pricing card glow (active plan) ── */
3638
  .pricing-glow {
3639
  position: relative;
3640
  }
3641
+
3642
  .pricing-glow::before {
3643
  content: '';
3644
  position: absolute;
 
3650
  filter: blur(12px);
3651
  animation: pricingPulse 3s ease-in-out infinite;
3652
  }
3653
+
3654
  @keyframes pricingPulse {
3655
+
3656
+ 0%,
3657
+ 100% {
3658
+ opacity: 0.3;
3659
+ }
3660
+
3661
+ 50% {
3662
+ opacity: 0.6;
3663
+ }
3664
  }
3665
 
3666
  /* ── Beta banner shimmer ── */
3667
  @keyframes betaShimmer {
3668
+ 0% {
3669
+ background-position: -200% center;
3670
+ }
3671
+
3672
+ 100% {
3673
+ background-position: 200% center;
3674
+ }
3675
  }
3676
+
3677
  .beta-shimmer {
3678
  background-size: 200% auto;
3679
  background-image: linear-gradient(90deg,
3680
+ transparent 0%,
3681
+ rgba(107, 163, 224, 0.06) 40%,
3682
+ rgba(165, 148, 232, 0.08) 60%,
3683
+ transparent 100%);
 
3684
  animation: betaShimmer 4s linear infinite;
3685
  }
3686
 
 
3718
  border-radius: 12px;
3719
  background: var(--color-surface-elevated);
3720
  border: 1px solid var(--color-border-strong);
3721
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.18), 0 0 0 1px rgba(255, 255, 255, 0.05);
3722
  backdrop-filter: blur(16px);
3723
  transform: translateX(-50%);
3724
  animation: selbar-in 0.2s ease;
3725
  pointer-events: auto;
3726
  }
3727
+
3728
  .selection-toolbar.is-hidden {
3729
  display: none;
3730
  }
3731
+
3732
  @keyframes selbar-in {
3733
+ from {
3734
+ opacity: 0;
3735
+ transform: translateX(-50%) translateY(6px);
3736
+ }
3737
+
3738
+ to {
3739
+ opacity: 1;
3740
+ transform: translateX(-50%) translateY(0);
3741
+ }
3742
  }
3743
+
3744
  .sel-tool-btn {
3745
  display: inline-flex;
3746
  align-items: center;
 
3757
  transition: all 0.15s ease;
3758
  white-space: nowrap;
3759
  }
3760
+
3761
  .sel-tool-btn:hover {
3762
  background: var(--color-surface);
3763
  color: var(--color-text-primary);
3764
  }
3765
+
3766
  .sel-tool-sep {
3767
  width: 1px;
3768
  height: 20px;
 
3780
  padding: 3px;
3781
  overflow: hidden;
3782
  }
3783
+
3784
  .summary-source-btn {
3785
  flex: 1;
3786
  padding: 8px 16px;
 
3794
  cursor: pointer;
3795
  transition: all 0.2s ease;
3796
  }
3797
+
3798
  .summary-source-btn:hover {
3799
  color: var(--color-text-primary);
3800
  }
3801
+
3802
  .summary-source-btn.active {
3803
  background: linear-gradient(135deg, var(--color-primary), var(--color-secondary));
3804
  color: var(--color-text-inverse);
 
3810
  margin-left: 4px;
3811
  opacity: 0.7;
3812
  }
3813
+
3814
  .editor-tab.active svg {
3815
  opacity: 1;
3816
  }
3817
+
3818
+ /* ── Error Badge on Tab ── */
3819
+ .tab-badge {
3820
+ display: inline-flex;
3821
+ align-items: center;
3822
+ justify-content: center;
3823
+ min-width: 18px;
3824
+ height: 18px;
3825
+ padding: 0 5px;
3826
+ border-radius: 10px;
3827
+ background: #ef4444;
3828
+ color: #fff;
3829
+ font-size: 10px;
3830
+ font-weight: 700;
3831
+ margin-inline-start: 6px;
3832
+ line-height: 1;
3833
+ animation: badgePulse 2s ease-in-out infinite;
3834
+ }
3835
+ @keyframes badgePulse {
3836
+ 0%, 100% { transform: scale(1); }
3837
+ 50% { transform: scale(1.1); }
3838
+ }
3839
+
3840
+ /* ── Find & Replace Bar ── */
3841
+ .find-replace-bar {
3842
+ padding: 8px 16px;
3843
+ background: var(--color-surface-elevated);
3844
+ border-bottom: 1px solid var(--color-border);
3845
+ display: flex;
3846
+ flex-direction: column;
3847
+ gap: 6px;
3848
+ }
3849
+ .find-replace-row {
3850
+ display: flex;
3851
+ align-items: center;
3852
+ gap: 6px;
3853
+ }
3854
+ .find-input {
3855
+ flex: 1;
3856
+ background: var(--color-bg);
3857
+ border: 1px solid var(--color-border);
3858
+ border-radius: 6px;
3859
+ padding: 6px 10px;
3860
+ color: var(--color-text-primary);
3861
+ font-size: 13px;
3862
+ font-family: inherit;
3863
+ outline: none;
3864
+ transition: border-color 0.2s;
3865
+ }
3866
+ .find-input:focus {
3867
+ border-color: var(--color-primary);
3868
+ }
3869
+ .find-count {
3870
+ font-size: 11px;
3871
+ color: var(--color-text-secondary);
3872
+ min-width: 35px;
3873
+ text-align: center;
3874
+ }
3875
+ .find-btn {
3876
+ background: var(--color-surface);
3877
+ border: 1px solid var(--color-border);
3878
+ border-radius: 5px;
3879
+ color: var(--color-text-secondary);
3880
+ padding: 4px 8px;
3881
+ cursor: pointer;
3882
+ font-size: 12px;
3883
+ transition: all 0.15s;
3884
+ font-family: inherit;
3885
+ }
3886
+ .find-btn:hover {
3887
+ background: var(--color-primary);
3888
+ color: #fff;
3889
+ border-color: var(--color-primary);
3890
+ }
3891
+
3892
+ /* ── Find Highlights ── */
3893
+ .find-highlight {
3894
+ background: rgba(255, 213, 79, 0.3);
3895
+ border-radius: 2px;
3896
+ }
3897
+ .find-highlight--active {
3898
+ background: rgba(255, 152, 0, 0.6);
3899
+ outline: 2px solid var(--color-primary);
3900
+ border-radius: 2px;
3901
+ }
3902
+
3903
+ /* ── Focus Mode ── */
3904
+ body.focus-mode .main-nav,
3905
+ body.focus-mode .suggestions-panel,
3906
+ body.focus-mode .docs-panel,
3907
+ body.focus-mode .editor-footer,
3908
+ body.focus-mode .editor-stats,
3909
+ body.focus-mode .editor-actions {
3910
+ opacity: 0;
3911
+ pointer-events: none;
3912
+ transition: opacity 0.4s ease;
3913
+ }
3914
+ body.focus-mode .editor-layout {
3915
+ grid-template-columns: 1fr !important;
3916
+ }
3917
+ body.focus-mode .format-toolbar {
3918
+ opacity: 0.3;
3919
+ transition: opacity 0.3s;
3920
+ }
3921
+ body.focus-mode .format-toolbar:hover {
3922
+ opacity: 1;
3923
+ }
3924
+ body.focus-mode .editor-surface {
3925
+ max-width: 800px;
3926
+ margin: 0 auto;
3927
+ }
3928
+
3929
+ /* ── Blockquote Styling in Editor ── */
3930
+ #editor-container blockquote {
3931
+ border-right: 4px solid var(--color-primary);
3932
+ margin: 8px 0;
3933
+ padding: 8px 16px;
3934
+ background: rgba(124, 131, 237, 0.08);
3935
+ border-radius: 0 6px 6px 0;
3936
+ color: var(--color-text-secondary);
3937
+ font-style: italic;
3938
+ }
3939
+
3940
+ /* ── HR Styling in Editor ── */
3941
+ #editor-container hr {
3942
+ border: none;
3943
+ height: 2px;
3944
+ background: linear-gradient(90deg, transparent, var(--color-border), transparent);
3945
+ margin: 16px 0;
3946
+ }
3947
+
3948
+ /* ── Tab Switch Animation ── */
3949
+ #write-area,
3950
+ #summarize-area,
3951
+ #dialect-area,
3952
+ #quran-area {
3953
+ animation: tabFadeIn 0.3s ease-out;
3954
+ }
3955
+ @keyframes tabFadeIn {
3956
+ from { opacity: 0; transform: translateY(6px); }
3957
+ to { opacity: 1; transform: translateY(0); }
3958
+ }
3959
+
3960
+ /* ── Typewriter Mode ── */
3961
+ .typewriter-mode {
3962
+ scroll-behavior: smooth;
3963
+ padding-top: 40vh !important;
3964
+ padding-bottom: 40vh !important;
3965
+ }
3966
+
3967
+ /* ── Collaboration Hint ── */
3968
+ .collab-hint {
3969
+ font-size: 10px;
3970
+ color: var(--color-text-secondary);
3971
+ opacity: 0.7;
3972
+ display: inline-flex;
3973
+ align-items: center;
3974
+ gap: 4px;
3975
+ }
3976
+ .collab-hint::before {
3977
+ content: '';
3978
+ width: 6px;
3979
+ height: 6px;
3980
+ border-radius: 50%;
3981
+ background: #22c55e;
3982
+ display: inline-block;
3983
+ }
3984
+
3985
+ /* ── Error Category Tabs ── */
3986
+ .error-tabs {
3987
+ display: flex;
3988
+ gap: 2px;
3989
+ margin-bottom: 10px;
3990
+ background: var(--color-border);
3991
+ border-radius: 8px;
3992
+ padding: 2px;
3993
+ overflow: hidden;
3994
+ }
3995
+ .error-tab {
3996
+ flex: 1;
3997
+ padding: 5px 4px;
3998
+ border: none;
3999
+ background: transparent;
4000
+ color: var(--color-text-secondary);
4001
+ font-size: 11px;
4002
+ font-weight: 600;
4003
+ cursor: pointer;
4004
+ border-radius: 6px;
4005
+ transition: all 0.2s;
4006
+ font-family: inherit;
4007
+ }
4008
+ .error-tab.active {
4009
+ background: var(--color-primary);
4010
+ color: #fff;
4011
+ }
4012
+ .error-tab:hover:not(.active) {
4013
+ background: var(--color-surface);
4014
+ }
4015
+
4016
+ /* ── Error Donut Chart ── */
4017
+ .error-chart-wrap {
4018
+ display: flex;
4019
+ align-items: center;
4020
+ justify-content: center;
4021
+ gap: 10px;
4022
+ margin-bottom: 10px;
4023
+ min-height: 0;
4024
+ }
4025
+ .error-donut {
4026
+ transform: rotate(-90deg);
4027
+ }
4028
+ .chart-legend {
4029
+ display: flex;
4030
+ flex-direction: column;
4031
+ gap: 4px;
4032
+ }
4033
+ .chart-legend-item {
4034
+ display: flex;
4035
+ align-items: center;
4036
+ gap: 5px;
4037
+ font-size: 11px;
4038
+ color: var(--color-text-secondary);
4039
+ }
4040
+ .chart-dot {
4041
+ width: 8px;
4042
+ height: 8px;
4043
+ border-radius: 50%;
4044
+ flex-shrink: 0;
4045
+ }
4046
+
4047
+ /* ── Dismiss All Button ── */
4048
+ .dismiss-all-btn {
4049
+ width: 100%;
4050
+ margin-top: 6px;
4051
+ font-size: 12px;
4052
+ color: var(--color-text-secondary);
4053
+ border: 1px dashed var(--color-border);
4054
+ border-radius: 6px;
4055
+ padding: 6px;
4056
+ text-align: center;
4057
+ }
4058
+ .dismiss-all-btn:hover {
4059
+ border-color: #ef4444;
4060
+ color: #ef4444;
4061
+ }
4062
+
4063
+ /* ── Score Sparkline ── */
4064
+ .sparkline {
4065
+ display: block;
4066
+ margin: 6px auto;
4067
+ }
src/index.html CHANGED
@@ -673,10 +673,10 @@
673
  <span id="auto-save-status" class="text-xs text-secondary" style="opacity:0;transition:opacity 0.3s;"></span>
674
  </div>
675
  <div class="editor-tabs-center">
676
- <button id="write-tab" onclick="switchTab('write')" class="editor-tab active" type="button"><svg class="tab-icon" width="15" height="15" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"/></svg>كتابة</button>
677
- <button id="summarize-tab" onclick="switchTab('summarize')" class="editor-tab" type="button"><svg class="tab-icon" width="15" height="15" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/></svg>تلخيص</button>
678
- <button id="dialect-tab" onclick="switchTab('dialect')" class="editor-tab" type="button"><svg class="tab-icon" width="15" height="15" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4"/></svg>تحويل للفصحى</button>
679
- <button id="quran-tab" onclick="switchTab('quran')" class="editor-tab" type="button"><svg class="tab-icon" width="15" height="15" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"/></svg>تدقيق النص القرآني</button>
680
  </div>
681
  <div class="flex items-center gap-2">
682
  <span id="doc-current-title" class="doc-current-title" style="display:none;"></span>
@@ -748,6 +748,12 @@
748
  <button id="fmt-ltr" class="fmt-btn" onclick="setDirection('ltr')" type="button" data-tooltip="يسار لليمين"><svg width="15" height="15" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 5H7a4 4 0 000 8h2m4-8v14m4-14v14M21 8l-4-3v6"/></svg></button>
749
  <button id="fmt-link" class="fmt-btn" onclick="insertLink()" type="button" data-tooltip="إدراج رابط"><svg width="15" height="15" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"/></svg></button>
750
  <button id="fmt-line-height" class="fmt-btn" onclick="cycleLineHeight()" type="button" data-tooltip="ارتفاع السطر"><svg width="15" height="15" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M20 4v4m0-4l-1.5 1.5M20 4l1.5 1.5M20 20v-4m0 4l-1.5-1.5M20 20l1.5-1.5"/></svg></button>
 
 
 
 
 
 
751
  </div>
752
  </div>
753
  <div class="fmt-divider"></div>
@@ -781,6 +787,21 @@
781
  <button class="fmt-btn" onclick="execFormat('removeFormat')" type="button" data-tooltip="مسح التنسيق"><svg width="15" height="15" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 4h12v3M12 4v16"/><path stroke-linecap="round" stroke-width="2.5" d="M5 19L19 5" opacity="0.6"/></svg></button>
782
  </div>
783
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
784
  <input type="file" id="doc-import-input" class="sr-only" accept=".txt,.docx,text/plain,application/vnd.openxmlformats-officedocument.wordprocessingml.document" aria-hidden="true">
785
  <div id="write-area">
786
  <div id="analysis-limit-banner" class="analysis-limit-banner is-hidden" role="status" aria-live="polite"></div>
@@ -803,14 +824,16 @@
803
  <div class="flex items-center gap-2"><span class="stat-dot stat-dot--punctuation" aria-hidden="true"></span><span class="text-sm text-secondary"><span id="punctuation-count">٠</span> ترقيم</span></div>
804
  <span class="stat-item--sep"></span>
805
  <span class="stat-item"><span id="char-count">٠</span> حرف</span>
806
- <span class="stat-item"><span id="sentence-count">٠</span> جملة</span>
 
807
  <span class="stat-item" style="cursor:pointer;" onclick="setWordGoalUI()" title="اضغط لتحديد هدف ع��د الكلمات"><span id="word-count" class="font-bold">٠</span> كلمة</span>
808
  <span id="word-goal-indicator" class="word-goal-badge" style="display:none;cursor:pointer;" onclick="setWordGoalUI()" title="اضغط لتغيير الهدف"></span>
809
  <span class="stat-item--sep"></span>
810
  <span class="stat-item">⏱ <span id="reading-time">٠</span> د قراءة</span>
811
- </div>
812
  <div class="editor-actions">
813
- <button id="doc-save-btn" class="doc-save-btn-icon btn-ghost" type="button" aria-label="حفظ المستند" title="حفظ">
 
814
  <svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-3m-1 4l-3 3m0 0l-3-3m3 3V4"/></svg>
815
  </button>
816
  <button onclick="clearEditor()" class="btn-ghost" type="button" title="مسح الكل">
@@ -986,9 +1009,17 @@
986
  <!-- LEFT: Sidebar (score + suggestions + import/export) -->
987
  <div class="sidebar-panel sidebar-desktop">
988
  <div class="surface-card sidebar-card">
989
- <h3 class="text-xl font-bold mb-4 text-right">الاقتراحات</h3>
 
 
 
 
 
 
 
990
  <div id="suggestions-list" class="suggestions-scroll" role="list" aria-live="polite" aria-label="اقتراحات التصحيح"></div>
991
  <button id="apply-all-btn" class="btn-primary apply-all-btn is-hidden" type="button">تطبيق الكل</button>
 
992
  </div>
993
  <div class="surface-card sidebar-card">
994
  <h3 class="text-xl font-bold mb-4 text-right">تقييم الكتابة</h3>
 
673
  <span id="auto-save-status" class="text-xs text-secondary" style="opacity:0;transition:opacity 0.3s;"></span>
674
  </div>
675
  <div class="editor-tabs-center">
676
+ <button id="write-tab" onclick="switchTab('write')" class="editor-tab active" data-tooltip="كتابة — الاختصار: Alt+1" type="button"><svg class="tab-icon" width="15" height="15" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"/></svg>كتابة<span id="error-badge" class="tab-badge" style="display:none"></span></button>
677
+ <button id="summarize-tab" onclick="switchTab('summarize')" class="editor-tab" data-tooltip="تلخيص — الاختصار: Alt+2" type="button"><svg class="tab-icon" width="15" height="15" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/></svg>تلخيص</button>
678
+ <button id="dialect-tab" onclick="switchTab('dialect')" class="editor-tab" data-tooltip="تحويل للفصحى — الاختصار: Alt+3" type="button"><svg class="tab-icon" width="15" height="15" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4"/></svg>تحويل للفصحى</button>
679
+ <button id="quran-tab" onclick="switchTab('quran')" class="editor-tab" data-tooltip="تدقيق القرآن — الاختصار: Ctrl+Q" type="button"><svg class="tab-icon" width="15" height="15" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"/></svg>تدقيق النص القرآني</button>
680
  </div>
681
  <div class="flex items-center gap-2">
682
  <span id="doc-current-title" class="doc-current-title" style="display:none;"></span>
 
748
  <button id="fmt-ltr" class="fmt-btn" onclick="setDirection('ltr')" type="button" data-tooltip="يسار لليمين"><svg width="15" height="15" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 5H7a4 4 0 000 8h2m4-8v14m4-14v14M21 8l-4-3v6"/></svg></button>
749
  <button id="fmt-link" class="fmt-btn" onclick="insertLink()" type="button" data-tooltip="إدراج رابط"><svg width="15" height="15" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"/></svg></button>
750
  <button id="fmt-line-height" class="fmt-btn" onclick="cycleLineHeight()" type="button" data-tooltip="ارتفاع السطر"><svg width="15" height="15" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M20 4v4m0-4l-1.5 1.5M20 4l1.5 1.5M20 20v-4m0 4l-1.5-1.5M20 20l1.5-1.5"/></svg></button>
751
+ <button id="fmt-indent" class="fmt-btn" onclick="execFormat('indent')" type="button" data-tooltip="مسافة بادئة"><svg width="15" height="15" fill="currentColor" viewBox="0 0 24 24"><path d="M3 5h18v2H3V5zm6 4h12v2H9V9zm0 4h12v2H9v-2zm-6 4h18v2H3v-2zM3 9l4 3-4 3V9z"/></svg></button>
752
+ <button id="fmt-outdent" class="fmt-btn" onclick="execFormat('outdent')" type="button" data-tooltip="إزالة المسافة البادئة"><svg width="15" height="15" fill="currentColor" viewBox="0 0 24 24"><path d="M3 5h18v2H3V5zm6 4h12v2H9V9zm0 4h12v2H9v-2zm-6 4h18v2H3v-2zM7 9l-4 3 4 3V9z"/></svg></button>
753
+ <button id="fmt-superscript" class="fmt-btn" onclick="execFormat('superscript')" type="button" data-tooltip="نص مرتفع"><svg width="15" height="15" fill="currentColor" viewBox="0 0 24 24"><path d="M16 7h2v1h-2v1h3V6h-2V5h2V4h-3v3zM5.47 13h2.06L10 7.22 12.47 13h2.06L10 3.3 5.47 13zM4 21l5-7h-3l5-7"/></svg></button>
754
+ <button id="fmt-subscript" class="fmt-btn" onclick="execFormat('subscript')" type="button" data-tooltip="نص منخفض"><svg width="15" height="15" fill="currentColor" viewBox="0 0 24 24"><path d="M16 18h2v1h-2v1h3v-4h-2v1h2v1h-3v1zM5.47 13h2.06L10 7.22 12.47 13h2.06L10 3.3 5.47 13z"/></svg></button>
755
+ <button id="fmt-blockquote" class="fmt-btn" onclick="formatBlockquote()" type="button" data-tooltip="اقتباس"><svg width="15" height="15" fill="currentColor" viewBox="0 0 24 24"><path d="M6 17h3l2-4V7H5v6h3l-2 4zm8 0h3l2-4V7h-6v6h3l-2 4z"/></svg></button>
756
+ <button id="fmt-hr" class="fmt-btn" onclick="execFormat('insertHorizontalRule')" type="button" data-tooltip="فاصل أفقي"><svg width="15" height="15" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-width="2" d="M3 12h18"/></svg></button>
757
  </div>
758
  </div>
759
  <div class="fmt-divider"></div>
 
787
  <button class="fmt-btn" onclick="execFormat('removeFormat')" type="button" data-tooltip="مسح التنسيق"><svg width="15" height="15" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 4h12v3M12 4v16"/><path stroke-linecap="round" stroke-width="2.5" d="M5 19L19 5" opacity="0.6"/></svg></button>
788
  </div>
789
  </div>
790
+ <div id="find-replace-bar" class="find-replace-bar" style="display:none;">
791
+ <div class="find-replace-row">
792
+ <input type="text" id="find-input" class="find-input" placeholder="بحث..." autocomplete="off">
793
+ <span id="find-count" class="find-count">0/0</span>
794
+ <button type="button" class="find-btn" onclick="findPrev()" data-tooltip="السابق">▲</button>
795
+ <button type="button" class="find-btn" onclick="findNext()" data-tooltip="التالي">▼</button>
796
+ <button type="button" class="find-btn" onclick="toggleReplace()" data-tooltip="استبدال">⇄</button>
797
+ <button type="button" class="find-btn" onclick="closeFindReplace()" data-tooltip="إغلاق">✕</button>
798
+ </div>
799
+ <div id="replace-row" class="find-replace-row" style="display:none;">
800
+ <input type="text" id="replace-input" class="find-input" placeholder="استبدال بـ..." autocomplete="off">
801
+ <button type="button" class="find-btn" onclick="replaceCurrent()">استبدال</button>
802
+ <button type="button" class="find-btn" onclick="replaceAll()">استبدال الكل</button>
803
+ </div>
804
+ </div>
805
  <input type="file" id="doc-import-input" class="sr-only" accept=".txt,.docx,text/plain,application/vnd.openxmlformats-officedocument.wordprocessingml.document" aria-hidden="true">
806
  <div id="write-area">
807
  <div id="analysis-limit-banner" class="analysis-limit-banner is-hidden" role="status" aria-live="polite"></div>
 
824
  <div class="flex items-center gap-2"><span class="stat-dot stat-dot--punctuation" aria-hidden="true"></span><span class="text-sm text-secondary"><span id="punctuation-count">٠</span> ترقيم</span></div>
825
  <span class="stat-item--sep"></span>
826
  <span class="stat-item"><span id="char-count">٠</span> حرف</span>
827
+ <span class="stat-item"><span id="paragraph-count">٠</span> فقرة</span>
828
+ <span class="stat-item"><span id="sentence-count">٠</span> جملة</span>
829
  <span class="stat-item" style="cursor:pointer;" onclick="setWordGoalUI()" title="اضغط لتحديد هدف ع��د الكلمات"><span id="word-count" class="font-bold">٠</span> كلمة</span>
830
  <span id="word-goal-indicator" class="word-goal-badge" style="display:none;cursor:pointer;" onclick="setWordGoalUI()" title="اضغط لتغيير الهدف"></span>
831
  <span class="stat-item--sep"></span>
832
  <span class="stat-item">⏱ <span id="reading-time">٠</span> د قراءة</span>
833
+ <span class="collab-hint" id="last-edit-hint"></span></div>
834
  <div class="editor-actions">
835
+ <button id="focus-mode-btn" class="btn-ghost" type="button" data-tooltip="وضع التركيز (Ctrl+Shift+F)" onclick="toggleFocusMode()"><svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/></svg></button>
836
+ <button id="doc-save-btn" class="doc-save-btn-icon btn-ghost" type="button" aria-label="حفظ المستند" title="حفظ">
837
  <svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-3m-1 4l-3 3m0 0l-3-3m3 3V4"/></svg>
838
  </button>
839
  <button onclick="clearEditor()" class="btn-ghost" type="button" title="مسح الكل">
 
1009
  <!-- LEFT: Sidebar (score + suggestions + import/export) -->
1010
  <div class="sidebar-panel sidebar-desktop">
1011
  <div class="surface-card sidebar-card">
1012
+ <h3 class="text-xl font-bold mb-3 text-right">الاقتراحات</h3>
1013
+ <div class="error-tabs">
1014
+ <button class="error-tab active" data-filter="all" onclick="filterErrors('all')">الكل</button>
1015
+ <button class="error-tab" data-filter="spelling" onclick="filterErrors('spelling')">إملائي</button>
1016
+ <button class="error-tab" data-filter="grammar" onclick="filterErrors('grammar')">نحوي</button>
1017
+ <button class="error-tab" data-filter="punctuation" onclick="filterErrors('punctuation')">ترقيم</button>
1018
+ </div>
1019
+ <div id="error-chart" class="error-chart-wrap"></div>
1020
  <div id="suggestions-list" class="suggestions-scroll" role="list" aria-live="polite" aria-label="اقتراحات التصحيح"></div>
1021
  <button id="apply-all-btn" class="btn-primary apply-all-btn is-hidden" type="button">تطبيق الكل</button>
1022
+ <button id="dismiss-filtered-btn" class="btn-ghost dismiss-all-btn is-hidden" type="button" onclick="dismissAllFiltered()">تجاهل الكل</button>
1023
  </div>
1024
  <div class="surface-card sidebar-card">
1025
  <h3 class="text-xl font-bold mb-4 text-right">تقييم الكتابة</h3>
src/js/format.js CHANGED
@@ -95,13 +95,369 @@ function cycleLineHeight() {
95
  const idx = heights.indexOf(_currentLineHeight);
96
  _currentLineHeight = heights[(idx + 1) % heights.length];
97
  editor.style.lineHeight = _currentLineHeight;
98
- // Show brief notification
99
  const btn = document.getElementById('fmt-line-height');
100
  if (btn) {
101
  btn.setAttribute('data-tooltip', 'ارتفاع السطر: ' + _currentLineHeight);
102
  }
103
  }
104
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
 
106
  /* ── Font family ── */
107
  function formatFont(fontName) {
@@ -308,6 +664,42 @@ function initFormatToolbar() {
308
  // Item 8: Color pickers
309
  initColorPicker('fmt-textcolor', 'foreColor', 'fmt-textcolor-bar');
310
  initColorPicker('fmt-highlight', 'hiliteColor', 'fmt-highlight-bar');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
311
  }
312
 
313
  /* ── Item 8: Color Picker ── */
@@ -443,3 +835,89 @@ function initDocSearch() {
443
  });
444
  });
445
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
  const idx = heights.indexOf(_currentLineHeight);
96
  _currentLineHeight = heights[(idx + 1) % heights.length];
97
  editor.style.lineHeight = _currentLineHeight;
 
98
  const btn = document.getElementById('fmt-line-height');
99
  if (btn) {
100
  btn.setAttribute('data-tooltip', 'ارتفاع السطر: ' + _currentLineHeight);
101
  }
102
  }
103
 
104
+ /* ── Blockquote ── */
105
+ function formatBlockquote() {
106
+ execFormat('formatBlock', 'blockquote');
107
+ }
108
+
109
+ /* ── Paragraph Spacing ── */
110
+ var _paragraphSpacing = 'normal';
111
+ function cycleParagraphSpacing() {
112
+ const editor = getEditorElement();
113
+ if (!editor) return;
114
+ const modes = { tight: '0.3em', normal: '0.8em', wide: '1.5em' };
115
+ const order = ['tight', 'normal', 'wide'];
116
+ const idx = order.indexOf(_paragraphSpacing);
117
+ _paragraphSpacing = order[(idx + 1) % order.length];
118
+ const val = modes[_paragraphSpacing];
119
+ editor.style.setProperty('--paragraph-spacing', val);
120
+ editor.querySelectorAll('div,p').forEach(el => {
121
+ el.style.marginBottom = val;
122
+ });
123
+ }
124
+
125
+ /* ── Find & Replace ── */
126
+ var _findMatches = [];
127
+ var _findIdx = -1;
128
+
129
+ function openFindReplace() {
130
+ const bar = document.getElementById('find-replace-bar');
131
+ if (bar) {
132
+ bar.style.display = 'block';
133
+ document.getElementById('find-input')?.focus();
134
+ }
135
+ }
136
+
137
+ function closeFindReplace() {
138
+ const bar = document.getElementById('find-replace-bar');
139
+ if (bar) bar.style.display = 'none';
140
+ clearHighlights();
141
+ _findMatches = [];
142
+ _findIdx = -1;
143
+ const countEl = document.getElementById('find-count');
144
+ if (countEl) countEl.textContent = '0/0';
145
+ }
146
+
147
+ function toggleReplace() {
148
+ const row = document.getElementById('replace-row');
149
+ if (row) row.style.display = row.style.display === 'none' ? 'flex' : 'none';
150
+ }
151
+
152
+ function doFind() {
153
+ const query = document.getElementById('find-input')?.value;
154
+ if (!query) { clearHighlights(); return; }
155
+ const editor = getEditorElement();
156
+ if (!editor) return;
157
+ clearHighlights();
158
+ const walker = document.createTreeWalker(editor, NodeFilter.SHOW_TEXT, null, false);
159
+ _findMatches = [];
160
+ let node;
161
+ while (node = walker.nextNode()) {
162
+ let idx = node.textContent.indexOf(query);
163
+ while (idx !== -1) {
164
+ _findMatches.push({ node, idx, len: query.length });
165
+ idx = node.textContent.indexOf(query, idx + 1);
166
+ }
167
+ }
168
+ // Highlight all matches
169
+ for (let i = _findMatches.length - 1; i >= 0; i--) {
170
+ const m = _findMatches[i];
171
+ const range = document.createRange();
172
+ range.setStart(m.node, m.idx);
173
+ range.setEnd(m.node, m.idx + m.len);
174
+ const span = document.createElement('span');
175
+ span.className = 'find-highlight';
176
+ range.surroundContents(span);
177
+ }
178
+ // Re-collect highlighted spans
179
+ _findMatches = Array.from(editor.querySelectorAll('.find-highlight'));
180
+ _findIdx = _findMatches.length > 0 ? 0 : -1;
181
+ updateFindCount();
182
+ if (_findIdx >= 0) highlightCurrent();
183
+ }
184
+
185
+ function findNext() {
186
+ if (_findMatches.length === 0) { doFind(); return; }
187
+ _findIdx = (_findIdx + 1) % _findMatches.length;
188
+ updateFindCount();
189
+ highlightCurrent();
190
+ }
191
+
192
+ function findPrev() {
193
+ if (_findMatches.length === 0) { doFind(); return; }
194
+ _findIdx = (_findIdx - 1 + _findMatches.length) % _findMatches.length;
195
+ updateFindCount();
196
+ highlightCurrent();
197
+ }
198
+
199
+ function highlightCurrent() {
200
+ _findMatches.forEach((el, i) => {
201
+ el.classList.toggle('find-highlight--active', i === _findIdx);
202
+ });
203
+ if (_findMatches[_findIdx]) {
204
+ _findMatches[_findIdx].scrollIntoView({ block: 'center', behavior: 'smooth' });
205
+ }
206
+ }
207
+
208
+ function updateFindCount() {
209
+ const el = document.getElementById('find-count');
210
+ if (el) el.textContent = _findMatches.length > 0 ? `${_findIdx + 1}/${_findMatches.length}` : '0/0';
211
+ }
212
+
213
+ function replaceCurrent() {
214
+ if (_findIdx < 0 || !_findMatches[_findIdx]) return;
215
+ const replaceVal = document.getElementById('replace-input')?.value || '';
216
+ _findMatches[_findIdx].textContent = replaceVal;
217
+ _findMatches[_findIdx].className = '';
218
+ _findMatches.splice(_findIdx, 1);
219
+ if (_findIdx >= _findMatches.length) _findIdx = 0;
220
+ updateFindCount();
221
+ if (_findMatches.length > 0) highlightCurrent();
222
+ }
223
+
224
+ function replaceAll() {
225
+ const replaceVal = document.getElementById('replace-input')?.value || '';
226
+ _findMatches.forEach(el => {
227
+ el.textContent = replaceVal;
228
+ el.className = '';
229
+ });
230
+ _findMatches = [];
231
+ _findIdx = -1;
232
+ updateFindCount();
233
+ }
234
+
235
+ function clearHighlights() {
236
+ const editor = getEditorElement();
237
+ if (!editor) return;
238
+ editor.querySelectorAll('.find-highlight').forEach(el => {
239
+ const parent = el.parentNode;
240
+ parent.replaceChild(document.createTextNode(el.textContent), el);
241
+ parent.normalize();
242
+ });
243
+ }
244
+
245
+ /* ── Focus Mode ── */
246
+ var _focusMode = false;
247
+ function toggleFocusMode() {
248
+ _focusMode = !_focusMode;
249
+ document.body.classList.toggle('focus-mode', _focusMode);
250
+ const btn = document.getElementById('focus-mode-btn');
251
+ if (btn) btn.classList.toggle('fmt-active', _focusMode);
252
+ }
253
+
254
+ /* ── Typewriter Mode ── */
255
+ var _typewriterMode = false;
256
+ function toggleTypewriterMode() {
257
+ _typewriterMode = !_typewriterMode;
258
+ const editor = getEditorElement();
259
+ if (!editor) return;
260
+ editor.classList.toggle('typewriter-mode', _typewriterMode);
261
+ if (_typewriterMode) {
262
+ editor.addEventListener('input', typewriterScroll);
263
+ editor.addEventListener('click', typewriterScroll);
264
+ } else {
265
+ editor.removeEventListener('input', typewriterScroll);
266
+ editor.removeEventListener('click', typewriterScroll);
267
+ }
268
+ }
269
+ function typewriterScroll() {
270
+ const sel = window.getSelection();
271
+ if (!sel.rangeCount) return;
272
+ const range = sel.getRangeAt(0);
273
+ const rect = range.getBoundingClientRect();
274
+ const editor = getEditorElement();
275
+ if (!editor) return;
276
+ const editorRect = editor.getBoundingClientRect();
277
+ const middle = editorRect.height / 2;
278
+ const offset = rect.top - editorRect.top - middle;
279
+ editor.scrollTop += offset;
280
+ }
281
+
282
+ /* ── Version History ── */
283
+ function saveVersion() {
284
+ const editor = getEditorElement();
285
+ if (!editor) return;
286
+ const versions = JSON.parse(localStorage.getItem('bayan_versions') || '[]');
287
+ versions.push({
288
+ timestamp: Date.now(),
289
+ content: editor.innerHTML,
290
+ preview: editor.textContent.substring(0, 50)
291
+ });
292
+ // Keep last 20 versions
293
+ if (versions.length > 20) versions.shift();
294
+ localStorage.setItem('bayan_versions', JSON.stringify(versions));
295
+ }
296
+
297
+ function showVersionHistory() {
298
+ const versions = JSON.parse(localStorage.getItem('bayan_versions') || '[]');
299
+ if (!versions.length) { alert('لا توجد نسخ سابقة'); return; }
300
+ let msg = 'اختر نسخة للاستعادة:\n\n';
301
+ versions.forEach((v, i) => {
302
+ const date = new Date(v.timestamp);
303
+ msg += `${i + 1}. ${date.toLocaleString('ar-EG')} — "${v.preview}..."\n`;
304
+ });
305
+ const choice = prompt(msg + '\nأدخل رقم النسخة:');
306
+ if (!choice) return;
307
+ const idx = parseInt(choice) - 1;
308
+ if (idx >= 0 && idx < versions.length) {
309
+ const editor = getEditorElement();
310
+ if (editor) {
311
+ pushUndoState();
312
+ editor.innerHTML = versions[idx].content;
313
+ }
314
+ }
315
+ }
316
+
317
+ /* ── Collaboration Hints (last edit timestamp) ── */
318
+ var _lastEditTime = null;
319
+ function updateLastEditTime() {
320
+ _lastEditTime = Date.now();
321
+ updateCollabHint();
322
+ }
323
+ function updateCollabHint() {
324
+ const el = document.getElementById('last-edit-hint');
325
+ if (!el || !_lastEditTime) return;
326
+ const diff = Math.floor((Date.now() - _lastEditTime) / 1000);
327
+ if (diff < 5) el.textContent = 'الآن';
328
+ else if (diff < 60) el.textContent = `منذ ${diff} ثانية`;
329
+ else if (diff < 3600) el.textContent = `منذ ${Math.floor(diff/60)} دقيقة`;
330
+ else el.textContent = `منذ ${Math.floor(diff/3600)} ساعة`;
331
+ }
332
+
333
+ /* ── Tashkeel (Diacritics) ── */
334
+ function addTashkeel() {
335
+ // This adds basic tashkeel placeholder — real implementation needs NLP
336
+ alert('ميزة التشكيل التلقائي قيد التطوير. سيتم إضافتها قريباً.');
337
+ }
338
+ function removeTashkeel() {
339
+ const editor = getEditorElement();
340
+ if (!editor) return;
341
+ pushUndoState();
342
+ const diacritics = /[\u064B-\u065F\u0670\u06D6-\u06ED]/g;
343
+ // Walk through text nodes only
344
+ const walker = document.createTreeWalker(editor, NodeFilter.SHOW_TEXT, null, false);
345
+ let node;
346
+ while (node = walker.nextNode()) {
347
+ const cleaned = node.textContent.replace(diacritics, '');
348
+ if (cleaned !== node.textContent) {
349
+ node.textContent = cleaned;
350
+ }
351
+ }
352
+ }
353
+
354
+ /* ── Number Conversion (Arabic ↔ Hindi) ── */
355
+ function convertToHindiNumerals() {
356
+ const editor = getEditorElement();
357
+ if (!editor) return;
358
+ pushUndoState();
359
+ const map = {'0':'٠','1':'١','2':'٢','3':'٣','4':'٤','5':'٥','6':'٦','7':'٧','8':'٨','9':'٩'};
360
+ walkTextNodes(editor, text => text.replace(/[0-9]/g, d => map[d]));
361
+ }
362
+ function convertToArabicNumerals() {
363
+ const editor = getEditorElement();
364
+ if (!editor) return;
365
+ pushUndoState();
366
+ const map = {'٠':'0','١':'1','٢':'2','٣':'3','٤':'4','٥':'5','٦':'6','٧':'7','٨':'8','٩':'9'};
367
+ walkTextNodes(editor, text => text.replace(/[٠-٩]/g, d => map[d]));
368
+ }
369
+ function walkTextNodes(root, fn) {
370
+ const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, null, false);
371
+ let node;
372
+ while (node = walker.nextNode()) {
373
+ const result = fn(node.textContent);
374
+ if (result !== node.textContent) node.textContent = result;
375
+ }
376
+ }
377
+
378
+ /* ── Text Cleanup ── */
379
+ function cleanupText() {
380
+ const editor = getEditorElement();
381
+ if (!editor) return;
382
+ pushUndoState();
383
+ walkTextNodes(editor, text => {
384
+ return text
385
+ .replace(/[\u064B-\u065F\u0670\u06D6-\u06ED]/g, '') // Remove diacritics
386
+ .replace(/\u200C/g, '') // Remove ZWNJ
387
+ .replace(/\u200D/g, '') // Remove ZWJ
388
+ .replace(/\u00A0/g, ' ') // NBSP → space
389
+ .replace(/ {2,}/g, ' ') // Multiple spaces → one
390
+ .replace(/\n{3,}/g, '\n\n'); // Multiple newlines → two
391
+ });
392
+ }
393
+
394
+ /* ── Error Badge Update ── */
395
+ function updateErrorBadge() {
396
+ const badge = document.getElementById('error-badge');
397
+ if (!badge) return;
398
+ const s = parseInt(document.getElementById('spelling-count')?.textContent) || 0;
399
+ const g = parseInt(document.getElementById('grammar-count')?.textContent) || 0;
400
+ const p = parseInt(document.getElementById('punctuation-count')?.textContent) || 0;
401
+ const total = s + g + p;
402
+ if (total > 0) {
403
+ badge.textContent = total;
404
+ badge.style.display = 'inline-flex';
405
+ } else {
406
+ badge.style.display = 'none';
407
+ }
408
+ }
409
+
410
+ /* ── Paragraph Count ── */
411
+ function updateParagraphCount() {
412
+ const editor = getEditorElement();
413
+ if (!editor) return;
414
+ const text = editor.textContent || '';
415
+ const paras = text.split(/\n\s*\n/).filter(p => p.trim().length > 0);
416
+ const count = text.trim().length > 0 ? Math.max(1, paras.length) : 0;
417
+ const el = document.getElementById('paragraph-count');
418
+ if (el) el.textContent = count.toLocaleString('ar-EG');
419
+ }
420
+
421
+ /* ── Tab Keyboard Shortcuts ── */
422
+ function initTabShortcuts() {
423
+ document.addEventListener('keydown', (e) => {
424
+ if (e.altKey && !e.ctrlKey) {
425
+ if (e.key === '1') { e.preventDefault(); switchTab('write'); }
426
+ else if (e.key === '2') { e.preventDefault(); switchTab('summarize'); }
427
+ else if (e.key === '3') { e.preventDefault(); switchTab('dialect'); }
428
+ else if (e.key === '4') { e.preventDefault(); switchTab('quran'); }
429
+ }
430
+ // Ctrl+H for Find & Replace
431
+ if (e.ctrlKey && e.key === 'h') {
432
+ e.preventDefault();
433
+ openFindReplace();
434
+ }
435
+ // Ctrl+Shift+F for Focus Mode
436
+ if (e.ctrlKey && e.shiftKey && e.key === 'F') {
437
+ e.preventDefault();
438
+ toggleFocusMode();
439
+ }
440
+ // Ctrl+K for link
441
+ if (e.ctrlKey && e.key === 'k') {
442
+ e.preventDefault();
443
+ insertLink();
444
+ }
445
+ });
446
+ }
447
+
448
+ /* ── Font Hover Preview ── */
449
+ function initFontHoverPreview() {
450
+ document.querySelectorAll('#fmt-font-menu .fmt-dropdown__item').forEach(item => {
451
+ item.addEventListener('mouseenter', () => {
452
+ const label = document.getElementById('fmt-font-label');
453
+ if (label) label.style.fontFamily = item.dataset.font;
454
+ });
455
+ item.addEventListener('mouseleave', () => {
456
+ const label = document.getElementById('fmt-font-label');
457
+ if (label) label.style.fontFamily = '';
458
+ });
459
+ });
460
+ }
461
 
462
  /* ── Font family ── */
463
  function formatFont(fontName) {
 
664
  // Item 8: Color pickers
665
  initColorPicker('fmt-textcolor', 'foreColor', 'fmt-textcolor-bar');
666
  initColorPicker('fmt-highlight', 'hiliteColor', 'fmt-highlight-bar');
667
+
668
+ // Tab keyboard shortcuts (Alt+1/2/3/4, Ctrl+H, Ctrl+Shift+F, Ctrl+K)
669
+ initTabShortcuts();
670
+
671
+ // Font hover preview
672
+ initFontHoverPreview();
673
+
674
+ // Find input listener
675
+ const findInput = document.getElementById('find-input');
676
+ if (findInput) {
677
+ findInput.addEventListener('input', doFind);
678
+ findInput.addEventListener('keydown', (e) => {
679
+ if (e.key === 'Enter') { e.shiftKey ? findPrev() : findNext(); }
680
+ if (e.key === 'Escape') closeFindReplace();
681
+ });
682
+ }
683
+
684
+ // Auto-save version every 2 minutes
685
+ setInterval(() => {
686
+ const ed = getEditorElement();
687
+ if (ed && ed.textContent.trim().length > 10) saveVersion();
688
+ }, 120000);
689
+
690
+ // Collaboration hint updater every 30s
691
+ setInterval(updateCollabHint, 30000);
692
+
693
+ // Hook editor input for error badge + paragraph count + last edit time
694
+ if (editor) {
695
+ const origInputHandler = editor.oninput;
696
+ editor.addEventListener('input', () => {
697
+ updateParagraphCount();
698
+ updateLastEditTime();
699
+ // Delay badge update to let analysis finish
700
+ setTimeout(updateErrorBadge, 2000);
701
+ });
702
+ }
703
  }
704
 
705
  /* ── Item 8: Color Picker ── */
 
835
  });
836
  });
837
  }
838
+
839
+ /* ── Error Filtering (Sidebar tabs) ── */
840
+ var _currentErrorFilter = 'all';
841
+ function filterErrors(type) {
842
+ _currentErrorFilter = type;
843
+ // Update tab active state
844
+ document.querySelectorAll('.error-tab').forEach(tab => {
845
+ tab.classList.toggle('active', tab.dataset.filter === type);
846
+ });
847
+ // Filter suggestion items
848
+ document.querySelectorAll('#suggestions-list .suggestion-item, #suggestions-list .sugg-card').forEach(item => {
849
+ if (type === 'all') {
850
+ item.style.display = '';
851
+ } else {
852
+ const itemType = item.dataset.type || item.getAttribute('data-error-type') || '';
853
+ item.style.display = itemType.includes(type) ? '' : 'none';
854
+ }
855
+ });
856
+ // Show/hide dismiss button
857
+ const dismissBtn = document.getElementById('dismiss-filtered-btn');
858
+ if (dismissBtn) dismissBtn.classList.toggle('is-hidden', type === 'all');
859
+ }
860
+
861
+ function dismissAllFiltered() {
862
+ const type = _currentErrorFilter;
863
+ if (type === 'all') return;
864
+ document.querySelectorAll('#suggestions-list .suggestion-item, #suggestions-list .sugg-card').forEach(item => {
865
+ const itemType = item.dataset.type || item.getAttribute('data-error-type') || '';
866
+ if (itemType.includes(type)) {
867
+ item.remove();
868
+ }
869
+ });
870
+ }
871
+
872
+ /* ── Error Breakdown Chart (SVG Donut) ── */
873
+ function renderErrorChart() {
874
+ const container = document.getElementById('error-chart');
875
+ if (!container) return;
876
+ const s = parseInt(document.getElementById('spelling-count')?.textContent) || 0;
877
+ const g = parseInt(document.getElementById('grammar-count')?.textContent) || 0;
878
+ const p = parseInt(document.getElementById('punctuation-count')?.textContent) || 0;
879
+ const total = s + g + p;
880
+ if (total === 0) {
881
+ container.innerHTML = '';
882
+ return;
883
+ }
884
+ const r = 30, cx = 40, cy = 40, c = 2 * Math.PI * r;
885
+ const segments = [
886
+ { val: s, color: '#ef4444', label: 'إملائي' },
887
+ { val: g, color: '#f59e0b', label: 'نحوي' },
888
+ { val: p, color: '#22c55e', label: 'ترقيم' },
889
+ ].filter(seg => seg.val > 0);
890
+ let offset = 0;
891
+ let paths = '';
892
+ segments.forEach(seg => {
893
+ const pct = seg.val / total;
894
+ const dash = c * pct;
895
+ const gap = c - dash;
896
+ paths += `<circle cx="${cx}" cy="${cy}" r="${r}" fill="none" stroke="${seg.color}" stroke-width="8" stroke-dasharray="${dash} ${gap}" stroke-dashoffset="${-offset}" opacity="0.85"/>`;
897
+ offset += dash;
898
+ });
899
+ let legend = segments.map(seg => `<span class="chart-legend-item"><span class="chart-dot" style="background:${seg.color}"></span>${seg.label}: ${seg.val}</span>`).join('');
900
+ container.innerHTML = `<svg width="80" height="80" viewBox="0 0 80 80" class="error-donut">${paths}<text x="${cx}" y="${cy}" text-anchor="middle" dominant-baseline="central" fill="var(--color-text-primary)" font-size="14" font-weight="700">${total}</text></svg><div class="chart-legend">${legend}</div>`;
901
+ }
902
+
903
+ /* ── Score History (Sparkline) ── */
904
+ var _scoreHistory = [];
905
+ function trackScore(score) {
906
+ _scoreHistory.push(score);
907
+ if (_scoreHistory.length > 20) _scoreHistory.shift();
908
+ renderSparkline();
909
+ }
910
+ function renderSparkline() {
911
+ const container = document.getElementById('score-sparkline');
912
+ if (!container || _scoreHistory.length < 2) return;
913
+ const w = 120, h = 30;
914
+ const max = Math.max(..._scoreHistory, 100);
915
+ const min = Math.min(..._scoreHistory, 0);
916
+ const range = max - min || 1;
917
+ const points = _scoreHistory.map((v, i) => {
918
+ const x = (i / (_scoreHistory.length - 1)) * w;
919
+ const y = h - ((v - min) / range) * h;
920
+ return `${x},${y}`;
921
+ }).join(' ');
922
+ container.innerHTML = `<svg width="${w}" height="${h}" class="sparkline"><polyline points="${points}" fill="none" stroke="var(--color-primary)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>`;
923
+ }