@mathscan/math-exercise-engine 1.2.1 → 1.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/style.css CHANGED
@@ -2630,6 +2630,806 @@ th.exercise-table-cell.exercise-table-cell--last-row:last-child {
2630
2630
  transform: none;
2631
2631
  }
2632
2632
 
2633
+ /**
2634
+ * Chart widget styles
2635
+ *
2636
+ * All colours and sizes are driven by CSS custom properties defined inside
2637
+ * `.math-exercise-renderer` (variables.css). Consumer apps can override any
2638
+ * of these variables on a parent selector to theme the chart without touching
2639
+ * engine code.
2640
+ *
2641
+ * Override surface example (student app):
2642
+ *
2643
+ * .my-exercise-wrapper {
2644
+ * --chart-bar-color: #4f46e5;
2645
+ * --chart-bar-correct-color: #22c55e;
2646
+ * --chart-bar-wrong-color: #ef4444;
2647
+ * --chart-height: 320px;
2648
+ * }
2649
+ *
2650
+ * `--chart-height` is the exception: bar charts write it inline, and pictographs
2651
+ * derive theirs from `--picto-cell-size` / `--picto-cell-gap`, so resize a
2652
+ * pictograph through those two tokens rather than through `--chart-height`.
2653
+ */
2654
+
2655
+ /* ── Token definitions (inside renderer scope) ─────────────────────────────── */
2656
+
2657
+ .math-exercise-renderer {
2658
+ /* Bar colours */
2659
+ --chart-bar-color: var(--exercise-accent, #705DC0);
2660
+ --chart-bar-correct-color: var(--exercise-correct, #22c55e);
2661
+ --chart-bar-wrong-color: var(--exercise-incorrect, #ef4444);
2662
+
2663
+ /* Axis / grid */
2664
+ --chart-grid-color: #e5e5e5;
2665
+ --chart-axis-color: var(--exercise-accent, #705DC0);
2666
+ --chart-axis-width: 2.35rem; /* width of the Y-axis label column */
2667
+
2668
+ /* Sizing */
2669
+ --chart-height: clamp(13rem, 34vw, 18rem);
2670
+ --chart-bar-slot-width: clamp(4.25rem, 11vw, 6rem);
2671
+ --chart-bar-width: clamp(2.7rem, 7vw, 4.1rem);
2672
+ --chart-bar-gap: clamp(0.65rem, 2.5vw, 1.5rem);
2673
+ --chart-content-width: 24rem;
2674
+
2675
+ /* Pictograph cells (fixed size — the plot height is derived from these) */
2676
+ --picto-cell-size: 2.6rem;
2677
+ --picto-cell-gap: 2px;
2678
+
2679
+ --chart-border-radius: 8px 8px 0 0;
2680
+
2681
+ /* Typography */
2682
+ --chart-font-size: clamp(0.78rem, 1.7vw, 1rem);
2683
+ --chart-label-font-size: clamp(0.85rem, 1.8vw, 1.08rem);
2684
+ --chart-font-weight: 500;
2685
+ --chart-label-min-height: 2.4em;
2686
+
2687
+ /* Handle */
2688
+ --chart-handle-size: clamp(0.9rem, 2vw, 1.15rem);
2689
+ }
2690
+
2691
+ /* ── Chart container (wraps title + chart grid) ────────────────────────────── */
2692
+
2693
+ .chart-container {
2694
+ display: flex;
2695
+ flex-direction: column;
2696
+ align-items: center;
2697
+ width: 100%;
2698
+ max-width: 560px;
2699
+ margin-block: 1rem;
2700
+ }
2701
+
2702
+ .chart-title {
2703
+ grid-column: 3;
2704
+ grid-row: 1;
2705
+ align-self: end;
2706
+ font-size: var(--chart-label-font-size);
2707
+ font-weight: 600;
2708
+ color: var(--exercise-accent, #6c5fc7);
2709
+ text-align: center;
2710
+ padding-block: 0.5rem 0.4rem;
2711
+ overflow-wrap: break-word;
2712
+ white-space: normal;
2713
+ }
2714
+
2715
+ .chart-title,
2716
+ .chart-plot,
2717
+ .chart-axis-x-numeric,
2718
+ .chart-axis-title--x-category,
2719
+ .chart-axis-x {
2720
+ width: var(--chart-content-width);
2721
+ min-width: var(--chart-content-width);
2722
+ }
2723
+
2724
+ /* ── Root container ─────────────────────────────────────────────────────────── */
2725
+
2726
+ .chart-scroll {
2727
+ width: 100%;
2728
+ }
2729
+
2730
+ .chart {
2731
+ display: grid;
2732
+ /* columns: [y-title] [y-axis] [plot] */
2733
+ grid-template-columns: auto var(--chart-axis-width) 1fr;
2734
+ grid-template-rows: auto 1fr auto auto; /* title/padding | plot | x labels | x title */
2735
+ align-items: end;
2736
+ width: max-content;
2737
+ min-width: 0;
2738
+ margin-inline: auto;
2739
+ padding-block-start: calc(var(--chart-font-size) * 0.75);
2740
+ }
2741
+
2742
+ /* Hidden axes: remove the matching plot-border axis line. */
2743
+
2744
+ .chart.chart--no-xaxis .chart-plot {
2745
+ border-bottom: none;
2746
+ }
2747
+
2748
+ .chart.chart--no-yaxis .chart-plot {
2749
+ border-left: none;
2750
+ border-inline-start: none;
2751
+ }
2752
+
2753
+ /* Vertical chart with no Y axis — collapse the Y-axis columns as well. */
2754
+
2755
+ .chart--no-yaxis:not(.chart--horizontal) {
2756
+ grid-template-columns: 1fr;
2757
+ }
2758
+
2759
+ /* ── Y axis ─────────────────────────────────────────────────────────────────── */
2760
+
2761
+ .chart-axis-tick-y,
2762
+ .chart-axis-tick-x-num {
2763
+ position: absolute;
2764
+ font-size: var(--chart-font-size);
2765
+ font-weight: var(--chart-font-weight);
2766
+ color: var(--chart-axis-color);
2767
+ line-height: 1;
2768
+ white-space: nowrap;
2769
+ pointer-events: none;
2770
+ user-select: none;
2771
+ }
2772
+
2773
+ .chart-axis-y {
2774
+ grid-column: 2;
2775
+ grid-row: 2;
2776
+ position: relative;
2777
+ height: var(--chart-height);
2778
+ width: var(--chart-axis-width);
2779
+ }
2780
+
2781
+ .chart-axis-tick-y {
2782
+ right: 0.45rem;
2783
+ transform: translateY(50%);
2784
+ }
2785
+
2786
+ /* ── Plot area ──────────────────────────────────────────────────────────────── */
2787
+
2788
+ .chart-plot {
2789
+ grid-column: 3;
2790
+ grid-row: 2;
2791
+ position: relative;
2792
+ height: var(--chart-height);
2793
+ border-left: 2px solid var(--chart-axis-color);
2794
+ border-bottom: 2px solid var(--chart-axis-color);
2795
+ overflow: hidden;
2796
+ }
2797
+
2798
+ /* ── Grid lines ─────────────────────────────────────────────────────────────── */
2799
+
2800
+ .chart-gridline {
2801
+ position: absolute;
2802
+ inset-inline: 0;
2803
+ height: 1px;
2804
+ background: var(--chart-grid-color);
2805
+ pointer-events: none;
2806
+ }
2807
+
2808
+ /* ── Bars container inside plot ─────────────────────────────────────────────── */
2809
+
2810
+ .chart-bars {
2811
+ position: absolute;
2812
+ inset: 0;
2813
+ display: flex;
2814
+ align-items: flex-end;
2815
+ justify-content: center;
2816
+ padding-inline: var(--chart-bar-gap);
2817
+ gap: var(--chart-bar-gap);
2818
+ }
2819
+
2820
+ /* ── Single bar wrapper ─────────────────────────────────────────────────────── */
2821
+
2822
+ .chart-bar-wrapper {
2823
+ flex: 0 0 var(--chart-bar-slot-width);
2824
+ width: var(--chart-bar-slot-width);
2825
+ display: flex;
2826
+ flex-direction: column;
2827
+ align-items: center;
2828
+ height: 100%;
2829
+ }
2830
+
2831
+ /* The full-height drag track (transparent, captures pointer events) */
2832
+
2833
+ .chart-bar-track {
2834
+ width: var(--chart-bar-width);
2835
+ height: 100%;
2836
+ position: relative;
2837
+ display: flex;
2838
+ flex-direction: column;
2839
+ justify-content: flex-end;
2840
+ }
2841
+
2842
+ .chart-bar-track--interactive {
2843
+ touch-action: none; /* prevent scroll while dragging */
2844
+ }
2845
+
2846
+ /* ── The filled bar ─────────────────────────────────────────────────────────── */
2847
+
2848
+ .chart-bar {
2849
+ width: 100%;
2850
+ /* Height driven by --bar-fill (0–1) × plot height; peek animation adds a
2851
+ temporary --peek-fill-offset to hint the bar is draggable */
2852
+ height: calc(min(1, var(--bar-fill, 0) + var(--peek-fill-offset, 0)) * 100%);
2853
+ background-color: var(--chart-bar-instance-color, var(--chart-bar-color));
2854
+ border-radius: var(--chart-border-radius);
2855
+ position: relative;
2856
+ transition: height 120ms ease, background-color 180ms ease, filter 180ms ease;
2857
+ }
2858
+
2859
+ .chart-bar--horizontal {
2860
+ border-radius: 0 8px 8px 0;
2861
+ }
2862
+
2863
+ /* Colour overrides based on validation */
2864
+
2865
+ .chart-bar-correct {
2866
+ background-color: var(--chart-bar-correct-color) !important;
2867
+ }
2868
+
2869
+ .chart-bar-incorrect {
2870
+ background-color: var(--chart-bar-wrong-color) !important;
2871
+ }
2872
+
2873
+ /* Hover tint on interactive bars */
2874
+
2875
+ .chart-bar-interactive {
2876
+ cursor: ns-resize;
2877
+ outline: none;
2878
+ touch-action: none;
2879
+ }
2880
+
2881
+ .chart--horizontal .chart-bar-interactive {
2882
+ cursor: ew-resize;
2883
+ }
2884
+
2885
+ .chart-bar-interactive.chart-bar-at-min {
2886
+ background-color: transparent;
2887
+ opacity: 0.7;
2888
+ }
2889
+
2890
+ .chart-bar-interactive.chart-bar-at-min.chart-bar-correct,
2891
+ .chart-bar-interactive.chart-bar-at-min.chart-bar-incorrect {
2892
+ background-color: transparent !important;
2893
+ }
2894
+
2895
+ .chart:not(.chart--horizontal) .chart-bar-interactive.chart-bar-at-min {
2896
+ min-height: var(--chart-handle-size);
2897
+ border-bottom: 3px dashed var(--chart-bar-instance-color, var(--chart-bar-color));
2898
+ }
2899
+
2900
+ .chart-bar-interactive:not(.chart-bar-correct):not(.chart-bar-incorrect):hover,
2901
+ .chart-bar-interactive:not(.chart-bar-correct):not(.chart-bar-incorrect):focus-visible,
2902
+ .chart-bar-interactive:not(.chart-bar-correct):not(.chart-bar-incorrect):active {
2903
+ background-color: color-mix(
2904
+ in srgb,
2905
+ var(--chart-bar-instance-color, var(--chart-bar-color)) 78%,
2906
+ white
2907
+ ) !important;
2908
+ }
2909
+
2910
+ .chart-bar-interactive:focus-visible {
2911
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--chart-axis-color) 24%, transparent);
2912
+ }
2913
+
2914
+ .chart-bar-interactive.chart-bar-at-min:not(.chart-bar-correct):not(.chart-bar-incorrect):is(:hover, :focus-visible, :active) {
2915
+ background-color: transparent !important;
2916
+ opacity: 1;
2917
+ }
2918
+
2919
+ .chart:not(.chart--horizontal) .chart-bar-interactive.chart-bar-at-min.chart-bar-correct {
2920
+ border-bottom-color: var(--chart-bar-correct-color);
2921
+ }
2922
+
2923
+ .chart:not(.chart--horizontal) .chart-bar-interactive.chart-bar-at-min.chart-bar-correct .chart-bar-handle {
2924
+ color: var(--chart-bar-correct-color);
2925
+ opacity: 1;
2926
+ }
2927
+
2928
+ .chart:not(.chart--horizontal) .chart-bar-interactive.chart-bar-at-min.chart-bar-incorrect {
2929
+ border-bottom-color: var(--chart-bar-wrong-color);
2930
+ }
2931
+
2932
+ .chart:not(.chart--horizontal) .chart-bar-interactive.chart-bar-at-min.chart-bar-incorrect .chart-bar-handle {
2933
+ color: var(--chart-bar-wrong-color);
2934
+ opacity: 1;
2935
+ }
2936
+
2937
+ /* ── Drag handle ────────────────────────────────────────────────────────────── */
2938
+
2939
+ .chart-bar-handle {
2940
+ position: absolute;
2941
+ top: 0.15rem;
2942
+ left: 50%;
2943
+ transform: translateX(-50%);
2944
+ width: var(--chart-handle-size);
2945
+ height: var(--chart-handle-size);
2946
+ color: white;
2947
+ pointer-events: none;
2948
+ z-index: 1;
2949
+ display: flex;
2950
+ align-items: center;
2951
+ justify-content: center;
2952
+ }
2953
+
2954
+ .chart-bar-handle svg {
2955
+ width: 100%;
2956
+ height: 100%;
2957
+ }
2958
+
2959
+ .chart:not(.chart--horizontal) .chart-bar-interactive.chart-bar-at-min .chart-bar-handle {
2960
+ top: 50%;
2961
+ transform: translateX(-50%) translateY(-50%);
2962
+ color: var(--chart-bar-instance-color, var(--chart-bar-color));
2963
+ opacity: 0.75;
2964
+ }
2965
+
2966
+ /* ── X axis numeric ticks (when categoryAxis is present) ───────────────────── */
2967
+
2968
+ .chart-axis-x-numeric {
2969
+ grid-column: 3;
2970
+ grid-row: 3;
2971
+ padding-top: 0.35rem;
2972
+ }
2973
+
2974
+ .chart-axis-x-numeric-ticks {
2975
+ position: relative;
2976
+ height: 1.4rem;
2977
+ overflow: visible;
2978
+ }
2979
+
2980
+ .chart-axis-tick-x-num {
2981
+ top: 0;
2982
+ transform: translateX(-50%);
2983
+ }
2984
+
2985
+ /* ── Axis titles (inline content from axis body { ... }) ────────────────────── */
2986
+
2987
+ .chart-axis-title {
2988
+ font-size: calc(var(--chart-font-size) * 0.9);
2989
+ color: var(--chart-axis-color);
2990
+ text-align: center;
2991
+ line-height: 1.2;
2992
+ }
2993
+
2994
+ .chart-axis-title--y,
2995
+ .chart-axis-title--y-h {
2996
+ align-self: center;
2997
+ justify-self: center;
2998
+ font-size: calc(var(--chart-font-size) * 0.85);
2999
+ writing-mode: vertical-rl;
3000
+ transform: rotate(180deg);
3001
+ white-space: nowrap;
3002
+ padding-inline: 0.2rem;
3003
+ }
3004
+
3005
+ .chart-axis-title--x,
3006
+ .chart-axis-title--x-category {
3007
+ font-size: calc(var(--chart-font-size) * 0.85);
3008
+ font-weight: var(--chart-font-weight);
3009
+ white-space: nowrap;
3010
+ }
3011
+
3012
+ .chart-axis-title--y {
3013
+ grid-column: 1;
3014
+ grid-row: 2 / 3;
3015
+ }
3016
+
3017
+ .chart-axis-title--x {
3018
+ display: block;
3019
+ margin-top: 0.35rem;
3020
+ }
3021
+
3022
+ /* X-axis title when bar labels are present (placed in grid row 4) */
3023
+
3024
+ .chart-axis-title--x-category {
3025
+ grid-column: 3;
3026
+ grid-row: 4;
3027
+ padding-block-start: 0.3rem;
3028
+ }
3029
+
3030
+ /* ── X axis (category or value labels row) ──────────────────────────────────── */
3031
+
3032
+ .chart-axis-x {
3033
+ grid-column: 3;
3034
+ grid-row: 3;
3035
+ display: flex;
3036
+ justify-content: center;
3037
+ padding-inline: var(--chart-bar-gap);
3038
+ gap: var(--chart-bar-gap);
3039
+ padding-top: 0.55rem;
3040
+ align-items: flex-start;
3041
+ }
3042
+
3043
+ .chart-axis-tick-x {
3044
+ flex: 0 0 var(--chart-bar-slot-width);
3045
+ min-width: 0;
3046
+ min-height: var(--chart-label-min-height);
3047
+ font-size: var(--chart-label-font-size);
3048
+ font-weight: var(--chart-font-weight);
3049
+ color: var(--chart-axis-color);
3050
+ line-height: 1.15;
3051
+ display: flex;
3052
+ justify-content: center;
3053
+ align-items: flex-start;
3054
+ }
3055
+
3056
+ .chart-axis-tick-x-inner {
3057
+ display: -webkit-box;
3058
+ -webkit-box-orient: vertical;
3059
+ -webkit-line-clamp: 3;
3060
+ line-clamp: 3;
3061
+ overflow: hidden;
3062
+ width: 100%;
3063
+ text-align: center;
3064
+ }
3065
+
3066
+ .chart-axis-tick-x .text-item {
3067
+ display: inline-block;
3068
+ max-width: 100%;
3069
+ overflow-wrap: anywhere;
3070
+ }
3071
+
3072
+ .chart-axis-tick-x .inline-input-droppable {
3073
+ max-width: 100%;
3074
+ }
3075
+
3076
+ .chart-axis-tick-x .inline-input,
3077
+ .chart-axis-tick-x .inline-input-chip {
3078
+ max-width: 100%;
3079
+ box-sizing: border-box;
3080
+ padding: 0.2rem 0.35rem;
3081
+ font-size: 0.82rem;
3082
+ line-height: 1.1;
3083
+ }
3084
+
3085
+ /* ═══════════════════════════════════════════════════════════════════════════
3086
+ HORIZONTAL BAR CHART (.chart--horizontal)
3087
+ Grid: [y-labels col] [plot col]
3088
+ [empty ] [x-axis row]
3089
+ ═══════════════════════════════════════════════════════════════════════════ */
3090
+
3091
+ .chart--horizontal {
3092
+ grid-template-columns: auto auto 1fr;
3093
+ grid-template-rows: auto 1fr auto;
3094
+ align-items: start; /* override vertical align-items: end */
3095
+ }
3096
+
3097
+ /* In horizontal mode, chart-title goes above the plot column */
3098
+
3099
+ .chart--horizontal .chart-title {
3100
+ grid-column: 3;
3101
+ grid-row: 1;
3102
+ align-self: end;
3103
+ width: max(100%, var(--chart-h-value-width, 16rem));
3104
+ }
3105
+
3106
+ /* ── Horizontal: Y-side category labels ─────────────────────────────────── */
3107
+
3108
+ .chart--horizontal .chart-axis-y--categories {
3109
+ grid-column: 2;
3110
+ grid-row: 2;
3111
+ display: flex;
3112
+ flex-direction: column;
3113
+ justify-content: flex-start;
3114
+ height: var(--chart-height);
3115
+ padding-block: var(--chart-bar-gap);
3116
+ gap: var(--chart-bar-gap);
3117
+ min-width: max-content;
3118
+ }
3119
+
3120
+ .chart--horizontal .chart-bar-y-label {
3121
+ flex: 0 0 var(--chart-bar-slot-width);
3122
+ height: var(--chart-bar-slot-width);
3123
+ display: flex;
3124
+ align-items: center;
3125
+ justify-content: flex-end;
3126
+ padding-inline-end: 0.6rem;
3127
+ padding-inline-start: 0.35rem;
3128
+ font-size: var(--chart-label-font-size);
3129
+ font-weight: var(--chart-font-weight);
3130
+ color: var(--exercise-text-primary, #1f2937);
3131
+ white-space: nowrap;
3132
+ }
3133
+
3134
+ /* ── Horizontal: plot area ───────────────────────────────────────────────── */
3135
+
3136
+ .chart--horizontal .chart-plot {
3137
+ grid-column: 3;
3138
+ grid-row: 2;
3139
+ width: max(100%, var(--chart-h-value-width, 16rem));
3140
+ border-inline-start: 2px solid var(--chart-axis-color);
3141
+ border-bottom: 2px solid var(--chart-axis-color);
3142
+ }
3143
+
3144
+ /* ── Horizontal: bars container ─────────────────────────────────────────── */
3145
+
3146
+ .chart--horizontal .chart-bars {
3147
+ flex-direction: column;
3148
+ align-items: flex-start;
3149
+ justify-content: flex-start;
3150
+ padding-inline: 0;
3151
+ padding-block: var(--chart-bar-gap);
3152
+ }
3153
+
3154
+ .chart--horizontal .chart-bar-wrapper {
3155
+ flex: 0 0 var(--chart-bar-slot-width);
3156
+ height: var(--chart-bar-slot-width);
3157
+ width: 100%;
3158
+ flex-direction: row;
3159
+ align-items: center;
3160
+ }
3161
+
3162
+ .chart--horizontal .chart-bar-track {
3163
+ width: 100%;
3164
+ height: var(--chart-bar-width);
3165
+ flex-direction: row;
3166
+ justify-content: flex-start;
3167
+ }
3168
+
3169
+ /* Bar grows along X (width instead of height) */
3170
+
3171
+ .chart--horizontal .chart-bar {
3172
+ width: calc(min(1, var(--bar-fill, 0) + var(--peek-fill-offset, 0)) * 100%);
3173
+ height: 100%;
3174
+ transition: width 120ms ease, background-color 180ms ease, filter 180ms ease;
3175
+ }
3176
+
3177
+ /* Interactive handle for horizontal: same SVG rotated 90° → ">>" */
3178
+
3179
+ .chart--horizontal .chart-bar-handle {
3180
+ top: 50%;
3181
+ left: unset;
3182
+ right: 0.55rem;
3183
+ transform: translateY(-50%) rotate(90deg);
3184
+ }
3185
+
3186
+ .chart--horizontal .chart-bar-interactive.chart-bar-at-min {
3187
+ min-width: var(--chart-handle-size);
3188
+ border-left: 3px dashed var(--chart-bar-instance-color, var(--chart-bar-color));
3189
+ }
3190
+
3191
+ .chart--horizontal .chart-bar-interactive.chart-bar-at-min .chart-bar-handle {
3192
+ left: 50%;
3193
+ right: auto;
3194
+ top: 50%;
3195
+ transform: translateX(-50%) translateY(-50%) rotate(90deg);
3196
+ color: var(--chart-bar-instance-color, var(--chart-bar-color));
3197
+ opacity: 0.75;
3198
+ }
3199
+
3200
+ .chart--horizontal .chart-bar-interactive.chart-bar-at-min.chart-bar-correct {
3201
+ border-left-color: var(--chart-bar-correct-color);
3202
+ }
3203
+
3204
+ .chart--horizontal .chart-bar-interactive.chart-bar-at-min.chart-bar-correct .chart-bar-handle {
3205
+ color: var(--chart-bar-correct-color);
3206
+ opacity: 1;
3207
+ }
3208
+
3209
+ .chart--horizontal .chart-bar-interactive.chart-bar-at-min.chart-bar-incorrect {
3210
+ border-left-color: var(--chart-bar-wrong-color);
3211
+ }
3212
+
3213
+ .chart--horizontal .chart-bar-interactive.chart-bar-at-min.chart-bar-incorrect .chart-bar-handle {
3214
+ color: var(--chart-bar-wrong-color);
3215
+ opacity: 1;
3216
+ }
3217
+
3218
+ /* ── Horizontal: grid (vertical lines) ──────────────────────────────────── */
3219
+
3220
+ .chart--horizontal .chart-gridline {
3221
+ width: 1px;
3222
+ height: 100%;
3223
+ top: 0;
3224
+ bottom: 0;
3225
+ inset-inline: unset;
3226
+ }
3227
+
3228
+ /* ── Horizontal: numeric X axis (below plot) ─────────────────────────────── */
3229
+
3230
+ .chart--horizontal .chart-axis-x-numeric {
3231
+ grid-column: 3;
3232
+ grid-row: 3;
3233
+ width: max(100%, var(--chart-h-value-width, 16rem));
3234
+ }
3235
+
3236
+ /* ── Horizontal: Y-axis title (shown above labels column) ───────────────── */
3237
+
3238
+ .chart-axis-title--y-h {
3239
+ grid-column: 1;
3240
+ grid-row: 2;
3241
+ }
3242
+
3243
+ /* ── Draggable-hint peek animation ──────────────────────────────────────────── */
3244
+
3245
+ @property --peek-fill-offset { syntax: '<number>'; inherits: false; initial-value: 0; }
3246
+
3247
+ @keyframes chart-bar-peek {
3248
+ 0%, 100% { --peek-fill-offset: 0; }
3249
+ 40% { --peek-fill-offset: 0.15; }
3250
+ 70% { --peek-fill-offset: 0.05; }
3251
+ }
3252
+
3253
+ @keyframes chart-bar-peek-zero {
3254
+ 0%, 100% { --peek-fill-offset: 0; background-color: transparent; opacity: 0.7; }
3255
+ 40% { --peek-fill-offset: 0.15; background-color: color-mix(in srgb, var(--chart-bar-instance-color, var(--chart-bar-color)) 72%, transparent); opacity: 0.95; }
3256
+ 70% { --peek-fill-offset: 0.05; background-color: color-mix(in srgb, var(--chart-bar-instance-color, var(--chart-bar-color)) 52%, transparent); opacity: 0.82; }
3257
+ }
3258
+
3259
+ @keyframes chart-bar-peek-handle-v {
3260
+ 0%, 100% { top: 50%; transform: translateX(-50%) translateY(-50%); color: var(--chart-bar-instance-color, var(--chart-bar-color)); opacity: 0.75; }
3261
+ 40%, 70% { top: 0.15rem; transform: translateX(-50%); color: white; opacity: 1; }
3262
+ }
3263
+
3264
+ @keyframes chart-bar-peek-handle-h {
3265
+ 0%, 100% { left: 50%; transform: translateX(-50%) translateY(-50%) rotate(90deg); color: var(--chart-bar-instance-color, var(--chart-bar-color)); opacity: 0.75; }
3266
+ 40%, 70% { left: calc(100% - 0.55rem); transform: translateX(-50%) translateY(-50%) rotate(90deg); color: white; opacity: 1; }
3267
+ }
3268
+
3269
+ .chart-bar--peek { transition: filter 180ms ease; animation: chart-bar-peek 0.7s ease-in-out 0.3s 2 both; }
3270
+
3271
+ .chart-bar-at-min.chart-bar--peek { animation: chart-bar-peek-zero 0.7s ease-in-out 0.3s 2 both; }
3272
+
3273
+ .chart:not(.chart--horizontal) .chart-bar-at-min.chart-bar--peek .chart-bar-handle { animation: chart-bar-peek-handle-v 0.7s ease-in-out 0.3s 2 both; }
3274
+
3275
+ .chart--horizontal .chart-bar-at-min.chart-bar--peek .chart-bar-handle { animation: chart-bar-peek-handle-h 0.7s ease-in-out 0.3s 2 both; }
3276
+
3277
+ /* ── Pictograph chart ────────────────────────────────────────────────────────── */
3278
+
3279
+ /* A vertical pictograph's --chart-height is emitted inline by
3280
+ computePictographChartStyle(), because the inline --chart-height that every chart
3281
+ already carries would outrank any rule written here. Overriding these two tokens
3282
+ still resizes the plot: the inline height is a calc() over them. */
3283
+
3284
+ /* The hint animation lifts and scales an icon past its cell, so the stack must not clip. */
3285
+
3286
+ .chart--pictograph .chart-plot {
3287
+ overflow: visible;
3288
+ }
3289
+
3290
+ .chart-picto-column-wrapper {
3291
+ display: flex;
3292
+ flex-direction: column;
3293
+ align-items: center;
3294
+ justify-content: flex-end;
3295
+ height: 100%;
3296
+ }
3297
+
3298
+ .chart-picto-column-wrapper--h {
3299
+ flex-direction: row;
3300
+ justify-content: flex-start;
3301
+ align-items: center;
3302
+ height: auto;
3303
+ width: 100%;
3304
+ }
3305
+
3306
+ .chart-picto-cells {
3307
+ display: flex;
3308
+ flex-direction: column-reverse;
3309
+ gap: var(--picto-cell-gap);
3310
+ }
3311
+
3312
+ .chart-picto-cells--h {
3313
+ flex-direction: row;
3314
+ gap: var(--picto-cell-gap);
3315
+ }
3316
+
3317
+ .picto-cell {
3318
+ width: var(--picto-cell-size);
3319
+ height: var(--picto-cell-size);
3320
+ display: flex;
3321
+ align-items: center;
3322
+ justify-content: center;
3323
+ border-radius: 4px;
3324
+ }
3325
+
3326
+ .picto-cell img {
3327
+ width: 85%;
3328
+ height: 85%;
3329
+ object-fit: contain;
3330
+ transition: filter 150ms ease, transform 150ms ease;
3331
+ }
3332
+
3333
+ .picto-cell--inactive img {
3334
+ filter: grayscale(100%) brightness(0.9);
3335
+ }
3336
+
3337
+ .picto-cell--interactive {
3338
+ cursor: pointer;
3339
+ }
3340
+
3341
+ .picto-cell--locked {
3342
+ cursor: default;
3343
+ }
3344
+
3345
+ .picto-cell--interactive:focus-visible {
3346
+ outline: 2px solid var(--chart-bar-color, #6366f1);
3347
+ outline-offset: 1px;
3348
+ }
3349
+
3350
+ /* Hover sur cellule ACTIVE : légère luminosité, reste clairement actif */
3351
+
3352
+ .picto-cell--interactive:not(.picto-cell--inactive):hover img {
3353
+ filter: brightness(1.18);
3354
+ }
3355
+
3356
+ /* Hover sur cellule INACTIVE : reste grise, juste légèrement plus lumineuse */
3357
+
3358
+ .picto-cell--inactive.picto-cell--interactive:hover img {
3359
+ filter: grayscale(100%) brightness(1.12);
3360
+ }
3361
+
3362
+ .picto-column--correct .picto-cell:not(.picto-cell--inactive) img {
3363
+ filter: drop-shadow(0 0 3px var(--exercise-correct, #22c55e));
3364
+ }
3365
+
3366
+ .picto-column--incorrect .picto-cell:not(.picto-cell--inactive) img {
3367
+ filter: drop-shadow(0 0 3px var(--exercise-incorrect, #ef4444));
3368
+ }
3369
+
3370
+ /* ── Pictograph validation animations ──────────────────────────────────────── */
3371
+
3372
+ @keyframes picto-pop {
3373
+ 0% { transform: scale(1); }
3374
+ 35% { transform: scale(1.32); }
3375
+ 60% { transform: scale(0.91); }
3376
+ 80% { transform: scale(1.07); }
3377
+ 100% { transform: scale(1); }
3378
+ }
3379
+
3380
+ @keyframes picto-jiggle {
3381
+ 0%, 100% { transform: translate(0, 0) rotate(0deg); }
3382
+ 12% { transform: translate(-5px, 0) rotate(-10deg); }
3383
+ 25% { transform: translate(5px, 0) rotate(10deg); }
3384
+ 37% { transform: translate(-4px, 0) rotate(-7deg); }
3385
+ 50% { transform: translate(4px, 0) rotate(7deg); }
3386
+ 62% { transform: translate(-2px, 0) rotate(-4deg); }
3387
+ 75% { transform: translate(2px, 0) rotate(4deg); }
3388
+ 87% { transform: translate(-1px, 0) rotate(-2deg); }
3389
+ }
3390
+
3391
+ .picto-cell--pop img {
3392
+ animation: picto-pop 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) both;
3393
+ }
3394
+
3395
+ .picto-cell--jiggle img {
3396
+ animation: picto-jiggle 0.55s ease-in-out both;
3397
+ }
3398
+
3399
+ @keyframes picto-hint {
3400
+ 0%, 100% { transform: translateY(0) scale(1); }
3401
+ 30% { transform: translateY(-5px) scale(1.12); }
3402
+ 55% { transform: translateY(-1px) scale(1.04); }
3403
+ 75% { transform: translateY(-3px) scale(1.09); }
3404
+ }
3405
+
3406
+ .picto-cell--hint img {
3407
+ animation: picto-hint 0.65s cubic-bezier(0.34, 1.2, 0.64, 1) both;
3408
+ }
3409
+
3410
+ /* ── Pictograph cell grid lines ─────────────────────────────────────────────── */
3411
+
3412
+ .picto-gridline {
3413
+ position: absolute;
3414
+ pointer-events: none;
3415
+ background: var(--chart-grid-color);
3416
+ }
3417
+
3418
+ /* Horizontal separators (between rows of picto cells) */
3419
+
3420
+ .picto-gridline--row {
3421
+ inset-inline: 0;
3422
+ height: 1px;
3423
+ }
3424
+
3425
+ /* Vertical separators (between picto columns) */
3426
+
3427
+ .picto-gridline--col {
3428
+ top: 0;
3429
+ bottom: 0;
3430
+ width: 1px;
3431
+ }
3432
+
2633
3433
  /* Responsive Overrides - must be last */
2634
3434
 
2635
3435
  /**