blinc_layout 0.5.0

Blinc layout engine - Flexbox layout powered by Taffy
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
//! Notch element for shapes with concave curves or sharp steps
//!
//! Provides a fluent API for creating shapes with concave (outward-bowing)
//! curves or sharp 90° step notches that `div()` cannot do.
//!
//! # The Notched Dropdown
//!
//! The primary use case - macOS-style menu bar dropdowns:
//!
//! ```text
//!     ╭──────────────╮  ← Menu bar
//! ╭───╯              ╰───╮
//! │                      │  ← Concave curves connect to bar
//! │    Content here      │
//! │                      │
//! ╰──────────────────────╯  ← Convex (standard) rounding
//! ```
//!
//! # Curved Example
//!
//! ```ignore
//! use blinc_layout::prelude::*;
//!
//! notch()
//!     .concave_top(24.0)    // Curved notch
//!     .rounded_bottom(16.0) // Standard rounding
//!     .bg(Color::BLACK)
//!     .child(text("Battery | 87% Charged"))
//! ```
//!
//! # Sharp Step Example
//!
//! ```ignore
//! notch()
//!     .step_top(24.0)       // Sharp 90° step notch
//!     .rounded_bottom(16.0)
//!     .bg(Color::BLACK)
//! ```
//!
//! # Animation with Signed Radius
//!
//! Use `.corner_*()` methods with signed values for smooth morphing:
//! - **Negative** = concave (curves outward)
//! - **Positive** = convex (standard rounding)  
//! - **Zero** = sharp corner (crossover point)
//!
//! ```ignore
//! stateful(|ctx| {
//!     let top_r = ctx.spring("top", if open { -24.0 } else { 16.0 });
//!     notch().corner_top(top_r).corner_bottom(16.0)
//! })
//! ```

use std::rc::Rc;

use blinc_core::{
    Brush, Color, CornerRadius, DrawContext, Gradient, Path, Rect, Shadow, Transform,
};
use taffy::{prelude::*, Overflow};

use crate::canvas::{CanvasBounds, CanvasRenderFn};
use crate::div::{ElementBuilder, ElementTypeId};
use crate::element::{Material, RenderLayer, RenderProps};
use crate::event_handler::EventHandlers;
use crate::tree::{LayoutNodeId, LayoutTree};
use crate::Div;

// =============================================================================
// Corner Configuration
// =============================================================================

/// Configuration for a single corner
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct CornerConfig {
    /// Radius/depth of the corner effect
    pub radius: f32,
    /// The style of corner
    pub style: CornerStyle,
}

/// The type of corner curve
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub enum CornerStyle {
    /// No corner effect (sharp 90° corner)
    #[default]
    None,
    /// Standard rounded corner (curves inward)
    Convex,
    /// Concave corner (curves outward)
    Concave,
    /// Sharp right-angle step/notch (extends outward with 90° angles)
    Step,
}

impl CornerConfig {
    /// No corner rounding
    pub const NONE: Self = Self {
        radius: 0.0,
        style: CornerStyle::None,
    };

    /// Create a convex corner - standard rounded corner (curves inward)
    pub fn convex(radius: f32) -> Self {
        Self {
            radius,
            style: CornerStyle::Convex,
        }
    }

    /// Create a concave corner - curves outward from the shape
    pub fn concave(radius: f32) -> Self {
        Self {
            radius,
            style: CornerStyle::Concave,
        }
    }

    /// Create a step corner - sharp right-angle notch
    ///
    /// ```text
    /// ┌───┐
    /// │   │  ← step with depth
    /// │   └───
    /// ```
    pub fn step(depth: f32) -> Self {
        Self {
            radius: depth,
            style: CornerStyle::Step,
        }
    }

    /// Check if this is a concave corner
    pub fn is_concave(&self) -> bool {
        matches!(self.style, CornerStyle::Concave)
    }

    /// Check if this is a step corner
    pub fn is_step(&self) -> bool {
        matches!(self.style, CornerStyle::Step)
    }
}

/// Configuration for all four corners
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct CornersConfig {
    pub top_left: CornerConfig,
    pub top_right: CornerConfig,
    pub bottom_right: CornerConfig,
    pub bottom_left: CornerConfig,
}

impl CornersConfig {
    /// All corners with no rounding
    pub const NONE: Self = Self {
        top_left: CornerConfig::NONE,
        top_right: CornerConfig::NONE,
        bottom_right: CornerConfig::NONE,
        bottom_left: CornerConfig::NONE,
    };

    /// Check if any corner has concave curves
    pub fn has_concave_curves(&self) -> bool {
        self.top_left.is_concave()
            || self.top_right.is_concave()
            || self.bottom_right.is_concave()
            || self.bottom_left.is_concave()
    }

    /// Check if any corner has step notches
    pub fn has_step_corners(&self) -> bool {
        self.top_left.is_step()
            || self.top_right.is_step()
            || self.bottom_right.is_step()
            || self.bottom_left.is_step()
    }

    /// Check if any corner requires custom path rendering (concave or step)
    pub fn needs_custom_rendering(&self) -> bool {
        self.has_concave_curves() || self.has_step_corners()
    }

    /// Convert to standard CornerRadius (ignoring concave/step)
    /// Used for simple shapes that don't need custom corners
    pub fn to_corner_radius(&self) -> CornerRadius {
        CornerRadius {
            top_left: if self.top_left.is_concave() || self.top_left.is_step() {
                0.0
            } else {
                self.top_left.radius
            },
            top_right: if self.top_right.is_concave() || self.top_right.is_step() {
                0.0
            } else {
                self.top_right.radius
            },
            bottom_right: if self.bottom_right.is_concave() || self.bottom_right.is_step() {
                0.0
            } else {
                self.bottom_right.radius
            },
            bottom_left: if self.bottom_left.is_concave() || self.bottom_left.is_step() {
                0.0
            } else {
                self.bottom_left.radius
            },
        }
    }
}

/// Configuration for a centered scoop/notch on an edge
///
/// This creates an inward curve in the center of an edge, like Apple's Dynamic Island.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct CenterScoop {
    /// Width of the scoop
    pub width: f32,
    /// Depth of the scoop (how far it curves inward)
    pub depth: f32,
    /// Corner radius at scoop entry/exit points for smoother transitions
    pub corner_radius: f32,
}

/// Configuration for a centered bulge (outward protrusion) on an edge
///
/// This creates an outward curve in the center of an edge - the opposite of a scoop.
/// Useful for highlighting active items in a bottom navigation bar.
///
/// ```text
/// ─────────╲         ╱─────────
///           ╲───────╱           ← bulge protrudes outward
/// ```
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct CenterBulge {
    /// Width of the bulge
    pub width: f32,
    /// Height of the bulge (how far it protrudes outward)
    pub height: f32,
    /// Corner radius at bulge entry/exit points for smoother transitions
    pub corner_radius: f32,
}

/// Configuration for a centered V-cut (sharp inward notch) on an edge
///
/// This creates a sharp V-shaped cut in the center of an edge.
/// The angle is determined by the ratio of width to depth.
///
/// ```text
/// ─────────╲    ╱─────────
///           ╲  ╱
///            ╲╱   ← sharp point at depth
/// ```
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct CenterCut {
    /// Width of the cut at the edge
    pub width: f32,
    /// Depth of the cut (how far it goes inward)
    pub depth: f32,
}

/// Configuration for a centered V-peak (sharp outward point) on an edge
///
/// This creates a sharp V-shaped peak protruding from the center of an edge.
/// The angle is determined by the ratio of width to height.
///
/// ```text
///            ╱╲   ← sharp point at height
///           ╱  ╲
/// ─────────╱    ╲─────────
/// ```
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct CenterPeak {
    /// Width of the peak at the base
    pub width: f32,
    /// Height of the peak (how far it protrudes outward)
    pub height: f32,
}

// =============================================================================
// Notch Element
// =============================================================================

/// A notch element for shapes with concave curves
///
/// Unlike `div()` which only supports convex (inward) corner rounding,
/// `Notch` supports concave (outward) curves for patterns like:
///
/// - **Menu bar dropdowns**: Concave curves at top connect to the bar
/// - **Tabs**: Concave curves where they meet adjacent content
pub struct Notch {
    // Corner configuration
    pub(crate) corners: CornersConfig,

    // Center scoop configuration (for Dynamic Island-style centered notches)
    pub(crate) top_center_scoop: Option<CenterScoop>,
    pub(crate) bottom_center_scoop: Option<CenterScoop>,

    // Center bulge configuration (for active menu highlighting)
    pub(crate) top_center_bulge: Option<CenterBulge>,
    pub(crate) bottom_center_bulge: Option<CenterBulge>,

    // Center cut configuration (for sharp V-shaped inward cuts)
    pub(crate) top_center_cut: Option<CenterCut>,
    pub(crate) bottom_center_cut: Option<CenterCut>,

    // Center peak configuration (for sharp V-shaped outward peaks)
    pub(crate) top_center_peak: Option<CenterPeak>,
    pub(crate) bottom_center_peak: Option<CenterPeak>,

    // Layout
    pub(crate) style: Style,
    pub(crate) children: Vec<Box<dyn ElementBuilder>>,

    // Visual
    pub(crate) background: Option<Brush>,
    pub(crate) border_color: Option<Color>,
    pub(crate) border_width: f32,
    pub(crate) shadow: Option<Shadow>,
    pub(crate) material: Option<Material>,
    pub(crate) opacity: f32,
    pub(crate) render_layer: RenderLayer,

    // Interaction
    pub(crate) event_handlers: EventHandlers,
    pub(crate) element_id: Option<String>,

    pub inner: Div,
}

impl Notch {
    /// Create a new notch element
    pub fn new() -> Self {
        Self {
            corners: CornersConfig::NONE,
            top_center_scoop: None,
            bottom_center_scoop: None,
            top_center_bulge: None,
            bottom_center_bulge: None,
            top_center_cut: None,
            bottom_center_cut: None,
            top_center_peak: None,
            bottom_center_peak: None,
            style: Style::default(),
            children: Vec::new(),
            background: None,
            border_color: None,
            border_width: 0.0,
            shadow: None,
            material: None,
            opacity: 1.0,
            render_layer: RenderLayer::default(),
            event_handlers: EventHandlers::default(),
            element_id: None,
            inner: Div::new(),
        }
    }

    // =========================================================================
    // Signed Radius Corners (for animation)
    // =========================================================================
    // Convention: negative = concave, positive = convex, zero = sharp
    // This allows smooth animation through the crossover point.

    /// Set top corners with signed radius
    ///
    /// - Negative: concave (curves outward) - the notch effect
    /// - Positive: convex (standard rounding)
    /// - Zero: sharp corner
    ///
    /// # Example
    ///
    /// ```ignore
    /// // Animate from convex to concave
    /// let r = ctx.spring("top", if open { -24.0 } else { 16.0 });
    /// notch().corner_top(r)
    /// ```
    pub fn corner_top(mut self, radius: f32) -> Self {
        if radius < 0.0 {
            self.corners.top_left = CornerConfig::concave(-radius);
            self.corners.top_right = CornerConfig::concave(-radius);
        } else {
            self.corners.top_left = CornerConfig::convex(radius);
            self.corners.top_right = CornerConfig::convex(radius);
        }
        self
    }

    /// Set bottom corners with signed radius
    pub fn corner_bottom(mut self, radius: f32) -> Self {
        if radius < 0.0 {
            self.corners.bottom_left = CornerConfig::concave(-radius);
            self.corners.bottom_right = CornerConfig::concave(-radius);
        } else {
            self.corners.bottom_left = CornerConfig::convex(radius);
            self.corners.bottom_right = CornerConfig::convex(radius);
        }
        self
    }

    /// Set left corners with signed radius
    pub fn corner_left(mut self, radius: f32) -> Self {
        if radius < 0.0 {
            self.corners.top_left = CornerConfig::concave(-radius);
            self.corners.bottom_left = CornerConfig::concave(-radius);
        } else {
            self.corners.top_left = CornerConfig::convex(radius);
            self.corners.bottom_left = CornerConfig::convex(radius);
        }
        self
    }

    /// Set right corners with signed radius
    pub fn corner_right(mut self, radius: f32) -> Self {
        if radius < 0.0 {
            self.corners.top_right = CornerConfig::concave(-radius);
            self.corners.bottom_right = CornerConfig::concave(-radius);
        } else {
            self.corners.top_right = CornerConfig::convex(radius);
            self.corners.bottom_right = CornerConfig::convex(radius);
        }
        self
    }

    /// Set top-left corner with signed radius
    pub fn corner_tl(mut self, radius: f32) -> Self {
        if radius < 0.0 {
            self.corners.top_left = CornerConfig::concave(-radius);
        } else {
            self.corners.top_left = CornerConfig::convex(radius);
        }
        self
    }

    /// Set top-right corner with signed radius
    pub fn corner_tr(mut self, radius: f32) -> Self {
        if radius < 0.0 {
            self.corners.top_right = CornerConfig::concave(-radius);
        } else {
            self.corners.top_right = CornerConfig::convex(radius);
        }
        self
    }

    /// Set bottom-right corner with signed radius
    pub fn corner_br(mut self, radius: f32) -> Self {
        if radius < 0.0 {
            self.corners.bottom_right = CornerConfig::concave(-radius);
        } else {
            self.corners.bottom_right = CornerConfig::convex(radius);
        }
        self
    }

    /// Set bottom-left corner with signed radius
    pub fn corner_bl(mut self, radius: f32) -> Self {
        if radius < 0.0 {
            self.corners.bottom_left = CornerConfig::concave(-radius);
        } else {
            self.corners.bottom_left = CornerConfig::convex(radius);
        }
        self
    }

    // =========================================================================
    // Concave Curves - The key differentiator from div()
    // =========================================================================

    /// Add concave curves on the left side (top-left and bottom-left corners bow left)
    ///
    /// ```text
    /// ╭───╯     
    /// │ ← curves bow left
    /// ╰───╮     
    /// ```
    pub fn concave_left(mut self, radius: f32) -> Self {
        self.corners.top_left = CornerConfig::concave(radius);
        self.corners.bottom_left = CornerConfig::concave(radius);
        self
    }

    /// Add concave curves on the right side (top-right and bottom-right corners bow right)
    ///
    /// ```text
    ///     ╰───╮
    ///         │ → curves bow right
    ///     ╭───╯
    /// ```
    pub fn concave_right(mut self, radius: f32) -> Self {
        self.corners.top_right = CornerConfig::concave(radius);
        self.corners.bottom_right = CornerConfig::concave(radius);
        self
    }

    /// Add concave curves on the top (top-left bows left, top-right bows right)
    ///
    /// This is the key method for creating notched dropdown shapes:
    /// ```text
    /// ╭───╯              ╰───╮
    /// │   ↑ bows left/right ↑│
    /// ```
    pub fn concave_top(mut self, radius: f32) -> Self {
        self.corners.top_left = CornerConfig::concave(radius);
        self.corners.top_right = CornerConfig::concave(radius);
        self
    }

    /// Add concave curves on the bottom (bottom-left bows left, bottom-right bows right)
    ///
    /// ```text
    /// │   ↓ bows left/right ↓│
    /// ╰───╮              ╭───╯
    /// ```
    pub fn concave_bottom(mut self, radius: f32) -> Self {
        self.corners.bottom_left = CornerConfig::concave(radius);
        self.corners.bottom_right = CornerConfig::concave(radius);
        self
    }

    /// Add concave curve to top-left corner (bows up-left)
    pub fn concave_tl(mut self, radius: f32) -> Self {
        self.corners.top_left = CornerConfig::concave(radius);
        self
    }

    /// Add concave curve to top-right corner (bows up-right)
    pub fn concave_tr(mut self, radius: f32) -> Self {
        self.corners.top_right = CornerConfig::concave(radius);
        self
    }

    /// Add concave curve to bottom-right corner (bows down-right)
    pub fn concave_br(mut self, radius: f32) -> Self {
        self.corners.bottom_right = CornerConfig::concave(radius);
        self
    }

    /// Add concave curve to bottom-left corner (bows down-left)
    pub fn concave_bl(mut self, radius: f32) -> Self {
        self.corners.bottom_left = CornerConfig::concave(radius);
        self
    }

    // =========================================================================
    // Step Corners (Sharp Right-Angle Notches)
    // =========================================================================

    /// Add sharp step notches to the top corners
    ///
    /// ```text
    ///     ┌──┐          ┌──┐
    ///     │  │          │  │
    /// ────┘  └──────────┘  └────
    /// ```
    pub fn step_top(mut self, depth: f32) -> Self {
        self.corners.top_left = CornerConfig::step(depth);
        self.corners.top_right = CornerConfig::step(depth);
        self
    }

    /// Add sharp step notches to the bottom corners
    pub fn step_bottom(mut self, depth: f32) -> Self {
        self.corners.bottom_left = CornerConfig::step(depth);
        self.corners.bottom_right = CornerConfig::step(depth);
        self
    }

    /// Add sharp step notches to the left corners
    pub fn step_left(mut self, depth: f32) -> Self {
        self.corners.top_left = CornerConfig::step(depth);
        self.corners.bottom_left = CornerConfig::step(depth);
        self
    }

    /// Add sharp step notches to the right corners
    pub fn step_right(mut self, depth: f32) -> Self {
        self.corners.top_right = CornerConfig::step(depth);
        self.corners.bottom_right = CornerConfig::step(depth);
        self
    }

    /// Add step notch to top-left corner
    pub fn step_tl(mut self, depth: f32) -> Self {
        self.corners.top_left = CornerConfig::step(depth);
        self
    }

    /// Add step notch to top-right corner
    pub fn step_tr(mut self, depth: f32) -> Self {
        self.corners.top_right = CornerConfig::step(depth);
        self
    }

    /// Add step notch to bottom-right corner
    pub fn step_br(mut self, depth: f32) -> Self {
        self.corners.bottom_right = CornerConfig::step(depth);
        self
    }

    /// Add step notch to bottom-left corner
    pub fn step_bl(mut self, depth: f32) -> Self {
        self.corners.bottom_left = CornerConfig::step(depth);
        self
    }

    // =========================================================================
    // Center Scoops (Dynamic Island-style)
    // =========================================================================

    /// Add a center scoop on the top edge
    ///
    /// Creates an inward curve in the center of the top edge, like Apple's Dynamic Island.
    /// The scoop curves INTO the shape (downward), not outward.
    ///
    /// ```text
    ///     ╭─────────╮
    ///     │ ╲_____╱ │  ← scoop cuts into top
    ///     │         │
    ///     ╰─────────╯
    /// ```
    pub fn center_scoop_top(mut self, width: f32, depth: f32) -> Self {
        self.top_center_scoop = Some(CenterScoop {
            width,
            depth,
            corner_radius: 0.0,
        });
        self
    }

    /// Add a center scoop on the top edge with rounded corners
    ///
    /// Like `center_scoop_top`, but with smooth corner transitions at scoop entry/exit.
    ///
    /// ```text
    ///     ╭─────────╮
    ///     │╭╲_____╱╮│  ← rounded corners at scoop edges
    ///     │         │
    ///     ╰─────────╯
    /// ```
    pub fn center_scoop_top_rounded(mut self, width: f32, depth: f32, corner_radius: f32) -> Self {
        self.top_center_scoop = Some(CenterScoop {
            width,
            depth,
            corner_radius,
        });
        self
    }

    /// Add a center scoop on the bottom edge
    ///
    /// Creates an inward curve in the center of the bottom edge.
    /// The scoop curves INTO the shape (upward), not outward.
    ///
    /// ```text
    ///     ╭─────────╮
    ///     │         │
    ///     │ ╱─────╲ │  ← scoop cuts into bottom
    ///     ╰─────────╯
    /// ```
    pub fn center_scoop_bottom(mut self, width: f32, depth: f32) -> Self {
        self.bottom_center_scoop = Some(CenterScoop {
            width,
            depth,
            corner_radius: 0.0,
        });
        self
    }

    /// Add a center scoop on the bottom edge with rounded corners
    ///
    /// Like `center_scoop_bottom`, but with smooth corner transitions at scoop entry/exit.
    pub fn center_scoop_bottom_rounded(
        mut self,
        width: f32,
        depth: f32,
        corner_radius: f32,
    ) -> Self {
        self.bottom_center_scoop = Some(CenterScoop {
            width,
            depth,
            corner_radius,
        });
        self
    }

    // =========================================================================
    // Center Bulges (Outward Protrusions)
    // =========================================================================

    /// Add a center bulge on the top edge
    ///
    /// Creates an outward curve in the center of the top edge - the opposite of a scoop.
    /// The bulge protrudes OUT of the shape (upward).
    ///
    /// ```text
    ///           ╱───────╲
    ///     ╭────╱         ╲────╮
    ///     │                   │  ← bulge protrudes upward
    ///     ╰───────────────────╯
    /// ```
    pub fn center_bulge_top(mut self, width: f32, height: f32) -> Self {
        self.top_center_bulge = Some(CenterBulge {
            width,
            height,
            corner_radius: 0.0,
        });
        self
    }

    /// Add a center bulge on the top edge with rounded corners
    ///
    /// Like `center_bulge_top`, but with smooth corner transitions at bulge entry/exit.
    pub fn center_bulge_top_rounded(mut self, width: f32, height: f32, corner_radius: f32) -> Self {
        self.top_center_bulge = Some(CenterBulge {
            width,
            height,
            corner_radius,
        });
        self
    }

    /// Add a center bulge on the bottom edge
    ///
    /// Creates an outward curve in the center of the bottom edge - the opposite of a scoop.
    /// The bulge protrudes OUT of the shape (downward).
    /// Perfect for highlighting active items in a bottom navigation bar.
    ///
    /// ```text
    ///     ╭───────────────────╮
    ///     │                   │
    ///     ╰────╲         ╱────╯
    ///           ╲───────╱       ← bulge protrudes downward (active indicator)
    /// ```
    pub fn center_bulge_bottom(mut self, width: f32, height: f32) -> Self {
        self.bottom_center_bulge = Some(CenterBulge {
            width,
            height,
            corner_radius: 0.0,
        });
        self
    }

    /// Add a center bulge on the bottom edge with rounded corners
    ///
    /// Like `center_bulge_bottom`, but with smooth corner transitions at bulge entry/exit.
    /// Great for smooth, polished active menu indicators.
    pub fn center_bulge_bottom_rounded(
        mut self,
        width: f32,
        height: f32,
        corner_radius: f32,
    ) -> Self {
        self.bottom_center_bulge = Some(CenterBulge {
            width,
            height,
            corner_radius,
        });
        self
    }

    // =========================================================================
    // Center Cuts (Sharp V-shaped Inward)
    // =========================================================================

    /// Add a center V-cut on the top edge
    ///
    /// Creates a sharp V-shaped cut in the center of the top edge.
    /// The cut goes INWARD (downward into the shape).
    ///
    /// ```text
    /// ─────────╲    ╱─────────
    ///           ╲  ╱
    ///            ╲╱   ← sharp point at depth
    /// ```
    ///
    /// The angle is determined by the width/depth ratio:
    /// - Narrow width + deep = steep angle
    /// - Wide width + shallow = gentle angle
    pub fn center_cut_top(mut self, width: f32, depth: f32) -> Self {
        self.top_center_cut = Some(CenterCut { width, depth });
        self
    }

    /// Add a center V-cut on the bottom edge
    ///
    /// Creates a sharp V-shaped cut in the center of the bottom edge.
    /// The cut goes INWARD (upward into the shape).
    ///
    /// ```text
    ///            ╱╲   ← sharp point at depth
    ///           ╱  ╲
    /// ─────────╱    ╲─────────
    /// ```
    pub fn center_cut_bottom(mut self, width: f32, depth: f32) -> Self {
        self.bottom_center_cut = Some(CenterCut { width, depth });
        self
    }

    // =========================================================================
    // Center Peaks (Sharp V-shaped Outward)
    // =========================================================================

    /// Add a center V-peak on the top edge
    ///
    /// Creates a sharp V-shaped peak protruding from the center of the top edge.
    /// The peak goes OUTWARD (upward out of the shape).
    ///
    /// ```text
    ///            ╱╲   ← sharp point at height
    ///           ╱  ╲
    /// ─────────╱    ╲─────────
    /// ```
    ///
    /// The angle is determined by the width/height ratio:
    /// - Narrow width + tall = steep angle
    /// - Wide width + short = gentle angle
    pub fn center_peak_top(mut self, width: f32, height: f32) -> Self {
        self.top_center_peak = Some(CenterPeak { width, height });
        self
    }

    /// Add a center V-peak on the bottom edge
    ///
    /// Creates a sharp V-shaped peak protruding from the center of the bottom edge.
    /// The peak goes OUTWARD (downward out of the shape).
    ///
    /// ```text
    /// ─────────╲    ╱─────────
    ///           ╲  ╱
    ///            ╲╱   ← sharp point at height
    /// ```
    pub fn center_peak_bottom(mut self, width: f32, height: f32) -> Self {
        self.bottom_center_peak = Some(CenterPeak { width, height });
        self
    }

    // =========================================================================
    // Inner Curves (Convex) - Standard rounded corners
    // =========================================================================

    /// Set uniform inner (convex) corner radius for all corners
    pub fn rounded(mut self, radius: f32) -> Self {
        self.corners.top_left = CornerConfig::convex(radius);
        self.corners.top_right = CornerConfig::convex(radius);
        self.corners.bottom_right = CornerConfig::convex(radius);
        self.corners.bottom_left = CornerConfig::convex(radius);
        self
    }

    /// Round only the bottom corners (inner/convex)
    pub fn rounded_bottom(mut self, radius: f32) -> Self {
        self.corners.bottom_left = CornerConfig::convex(radius);
        self.corners.bottom_right = CornerConfig::convex(radius);
        self
    }

    /// Round only the top corners (inner/convex)
    pub fn rounded_top(mut self, radius: f32) -> Self {
        self.corners.top_left = CornerConfig::convex(radius);
        self.corners.top_right = CornerConfig::convex(radius);
        self
    }

    /// Round only the left corners (inner/convex)
    pub fn rounded_left(mut self, radius: f32) -> Self {
        self.corners.top_left = CornerConfig::convex(radius);
        self.corners.bottom_left = CornerConfig::convex(radius);
        self
    }

    /// Round only the right corners (inner/convex)
    pub fn rounded_right(mut self, radius: f32) -> Self {
        self.corners.top_right = CornerConfig::convex(radius);
        self.corners.bottom_right = CornerConfig::convex(radius);
        self
    }

    /// Round top-left corner (inner/convex)
    pub fn rounded_tl(mut self, radius: f32) -> Self {
        self.corners.top_left = CornerConfig::convex(radius);
        self
    }

    /// Round top-right corner (inner/convex)
    pub fn rounded_tr(mut self, radius: f32) -> Self {
        self.corners.top_right = CornerConfig::convex(radius);
        self
    }

    /// Round bottom-right corner (inner/convex)
    pub fn rounded_br(mut self, radius: f32) -> Self {
        self.corners.bottom_right = CornerConfig::convex(radius);
        self
    }

    /// Round bottom-left corner (inner/convex)
    pub fn rounded_bl(mut self, radius: f32) -> Self {
        self.corners.bottom_left = CornerConfig::convex(radius);
        self
    }

    /// Make fully rounded (pill shape)
    pub fn rounded_full(mut self) -> Self {
        self.corners.top_left = CornerConfig::convex(9999.0);
        self.corners.top_right = CornerConfig::convex(9999.0);
        self.corners.bottom_right = CornerConfig::convex(9999.0);
        self.corners.bottom_left = CornerConfig::convex(9999.0);
        self
    }

    // =========================================================================
    // Size & Layout
    // =========================================================================

    /// Set fixed width
    pub fn w(mut self, width: f32) -> Self {
        self.style.size.width = Dimension::Length(width);
        self
    }

    /// Set fixed height
    pub fn h(mut self, height: f32) -> Self {
        self.style.size.height = Dimension::Length(height);
        self
    }

    /// Set both width and height to the same value
    pub fn size(mut self, size: f32) -> Self {
        self.style.size.width = Dimension::Length(size);
        self.style.size.height = Dimension::Length(size);
        self
    }

    /// Set width to 100%
    pub fn w_full(mut self) -> Self {
        self.style.size.width = Dimension::Percent(1.0);
        self
    }

    /// Set height to 100%
    pub fn h_full(mut self) -> Self {
        self.style.size.height = Dimension::Percent(1.0);
        self
    }

    /// Set width to fit content
    pub fn w_fit(mut self) -> Self {
        self.style.size.width = Dimension::Auto;
        self
    }

    /// Set height to fit content
    pub fn h_fit(mut self) -> Self {
        self.style.size.height = Dimension::Auto;
        self
    }

    /// Set minimum width
    pub fn min_w(mut self, width: f32) -> Self {
        self.style.min_size.width = Dimension::Length(width);
        self
    }

    /// Set minimum height
    pub fn min_h(mut self, height: f32) -> Self {
        self.style.min_size.height = Dimension::Length(height);
        self
    }

    /// Set maximum width
    pub fn max_w(mut self, width: f32) -> Self {
        self.style.max_size.width = Dimension::Length(width);
        self
    }

    /// Set maximum height
    pub fn max_h(mut self, height: f32) -> Self {
        self.style.max_size.height = Dimension::Length(height);
        self
    }

    // =========================================================================
    // Padding
    // =========================================================================

    /// Set uniform padding on all sides
    pub fn p(mut self, padding: f32) -> Self {
        self.style.padding = taffy::Rect {
            left: LengthPercentage::Length(padding),
            right: LengthPercentage::Length(padding),
            top: LengthPercentage::Length(padding),
            bottom: LengthPercentage::Length(padding),
        };
        self
    }

    /// Set horizontal padding (left and right)
    pub fn px(mut self, padding: f32) -> Self {
        self.style.padding.left = LengthPercentage::Length(padding);
        self.style.padding.right = LengthPercentage::Length(padding);
        self
    }

    /// Set vertical padding (top and bottom)
    pub fn py(mut self, padding: f32) -> Self {
        self.style.padding.top = LengthPercentage::Length(padding);
        self.style.padding.bottom = LengthPercentage::Length(padding);
        self
    }

    /// Set top padding
    pub fn pt(mut self, padding: f32) -> Self {
        self.style.padding.top = LengthPercentage::Length(padding);
        self
    }

    /// Set bottom padding
    pub fn pb(mut self, padding: f32) -> Self {
        self.style.padding.bottom = LengthPercentage::Length(padding);
        self
    }

    /// Set left padding
    pub fn pl(mut self, padding: f32) -> Self {
        self.style.padding.left = LengthPercentage::Length(padding);
        self
    }

    /// Set right padding
    pub fn pr(mut self, padding: f32) -> Self {
        self.style.padding.right = LengthPercentage::Length(padding);
        self
    }

    // =========================================================================
    // Margin
    // =========================================================================

    /// Set uniform margin on all sides
    pub fn m(mut self, margin: f32) -> Self {
        self.style.margin = taffy::Rect {
            left: LengthPercentageAuto::Length(margin),
            right: LengthPercentageAuto::Length(margin),
            top: LengthPercentageAuto::Length(margin),
            bottom: LengthPercentageAuto::Length(margin),
        };
        self
    }

    /// Set horizontal margin (left and right)
    pub fn mx(mut self, margin: f32) -> Self {
        self.style.margin.left = LengthPercentageAuto::Length(margin);
        self.style.margin.right = LengthPercentageAuto::Length(margin);
        self
    }

    /// Set vertical margin (top and bottom)
    pub fn my(mut self, margin: f32) -> Self {
        self.style.margin.top = LengthPercentageAuto::Length(margin);
        self.style.margin.bottom = LengthPercentageAuto::Length(margin);
        self
    }

    /// Center horizontally with auto margins
    pub fn mx_auto(mut self) -> Self {
        self.style.margin.left = LengthPercentageAuto::Auto;
        self.style.margin.right = LengthPercentageAuto::Auto;
        self
    }

    // =========================================================================
    // Flexbox
    // =========================================================================

    /// Set flex direction to column
    pub fn flex_col(mut self) -> Self {
        self.style.display = Display::Flex;
        self.style.flex_direction = FlexDirection::Column;
        self
    }

    /// Set flex direction to row
    pub fn flex_row(mut self) -> Self {
        self.style.display = Display::Flex;
        self.style.flex_direction = FlexDirection::Row;
        self
    }

    /// Set gap between children
    pub fn gap(mut self, gap: f32) -> Self {
        self.style.gap = taffy::Size {
            width: LengthPercentage::Length(gap),
            height: LengthPercentage::Length(gap),
        };
        self
    }

    /// Center children both horizontally and vertically
    pub fn flex_center(mut self) -> Self {
        self.style.display = Display::Flex;
        self.style.justify_content = Some(JustifyContent::Center);
        self.style.align_items = Some(AlignItems::Center);
        self
    }

    /// Justify content to start
    pub fn justify_start(mut self) -> Self {
        self.style.justify_content = Some(JustifyContent::Start);
        self
    }

    /// Justify content to center
    pub fn justify_center(mut self) -> Self {
        self.style.justify_content = Some(JustifyContent::Center);
        self
    }

    /// Justify content to end
    pub fn justify_end(mut self) -> Self {
        self.style.justify_content = Some(JustifyContent::End);
        self
    }

    /// Justify content with space between
    pub fn justify_between(mut self) -> Self {
        self.style.justify_content = Some(JustifyContent::SpaceBetween);
        self
    }

    /// Align items to start
    pub fn items_start(mut self) -> Self {
        self.style.align_items = Some(AlignItems::Start);
        self
    }

    /// Align items to center
    pub fn items_center(mut self) -> Self {
        self.style.align_items = Some(AlignItems::Center);
        self
    }

    /// Align items to end
    pub fn items_end(mut self) -> Self {
        self.style.align_items = Some(AlignItems::End);
        self
    }

    /// Flex grow
    pub fn flex_grow(mut self) -> Self {
        self.style.flex_grow = 1.0;
        self
    }

    /// Flex shrink
    pub fn flex_shrink(mut self) -> Self {
        self.style.flex_shrink = 1.0;
        self
    }

    /// Flex none (don't grow or shrink)
    pub fn flex_none(mut self) -> Self {
        self.style.flex_grow = 0.0;
        self.style.flex_shrink = 0.0;
        self
    }

    // =========================================================================
    // Positioning
    // =========================================================================

    /// Set position to absolute
    pub fn absolute(mut self) -> Self {
        self.style.position = Position::Absolute;
        self
    }

    /// Set position to relative
    pub fn relative(mut self) -> Self {
        self.style.position = Position::Relative;
        self
    }

    /// Set top offset
    pub fn top(mut self, offset: f32) -> Self {
        self.style.inset.top = LengthPercentageAuto::Length(offset);
        self
    }

    /// Set bottom offset
    pub fn bottom(mut self, offset: f32) -> Self {
        self.style.inset.bottom = LengthPercentageAuto::Length(offset);
        self
    }

    /// Set left offset
    pub fn left(mut self, offset: f32) -> Self {
        self.style.inset.left = LengthPercentageAuto::Length(offset);
        self
    }

    /// Set right offset
    pub fn right(mut self, offset: f32) -> Self {
        self.style.inset.right = LengthPercentageAuto::Length(offset);
        self
    }

    // =========================================================================
    // Visual Styling
    // =========================================================================

    /// Set background color
    pub fn bg(mut self, color: impl Into<Color>) -> Self {
        self.background = Some(Brush::Solid(color.into()));
        self
    }

    /// Set background brush (color, gradient, or glass)
    pub fn background(mut self, brush: impl Into<Brush>) -> Self {
        self.background = Some(brush.into());
        self
    }

    /// Set border color and width
    pub fn border(mut self, width: f32, color: impl Into<Color>) -> Self {
        self.border_width = width;
        self.border_color = Some(color.into());
        self
    }

    /// Set border width only
    pub fn border_width(mut self, width: f32) -> Self {
        self.border_width = width;
        self
    }

    /// Set border color only
    pub fn border_color(mut self, color: impl Into<Color>) -> Self {
        self.border_color = Some(color.into());
        self
    }

    /// Add drop shadow
    pub fn shadow(mut self, shadow: Shadow) -> Self {
        self.shadow = Some(shadow);
        self
    }

    /// Add a small shadow
    pub fn shadow_sm(mut self) -> Self {
        self.shadow = Some(Shadow::new(0.0, 1.0, 3.0, Color::rgba(0.0, 0.0, 0.0, 0.1)));
        self
    }

    /// Add a medium shadow
    pub fn shadow_md(mut self) -> Self {
        self.shadow = Some(Shadow::new(0.0, 4.0, 6.0, Color::rgba(0.0, 0.0, 0.0, 0.1)));
        self
    }

    /// Add a large shadow
    pub fn shadow_lg(mut self) -> Self {
        self.shadow = Some(Shadow::new(
            0.0,
            10.0,
            15.0,
            Color::rgba(0.0, 0.0, 0.0, 0.1),
        ));
        self
    }

    /// Set opacity
    pub fn opacity(mut self, opacity: f32) -> Self {
        self.opacity = opacity;
        self
    }

    /// Apply glass/frosted material effect
    pub fn glass(mut self) -> Self {
        self.material = Some(Material::Glass(Default::default()));
        self.render_layer = RenderLayer::Glass;
        self
    }

    // =========================================================================
    // Overflow
    // =========================================================================

    /// Clip overflowing content
    pub fn overflow_clip(mut self) -> Self {
        self.style.overflow.x = Overflow::Hidden;
        self.style.overflow.y = Overflow::Hidden;
        self
    }

    /// Allow content to overflow
    pub fn overflow_visible(mut self) -> Self {
        self.style.overflow.x = Overflow::Visible;
        self.style.overflow.y = Overflow::Visible;
        self
    }

    // =========================================================================
    // Children
    // =========================================================================

    /// Add a child element
    pub fn child(mut self, child: impl ElementBuilder + 'static) -> Self {
        self.children.push(Box::new(child));
        self
    }

    /// Add multiple children from an iterator
    pub fn children(
        mut self,
        children: impl IntoIterator<Item = impl ElementBuilder + 'static>,
    ) -> Self {
        for child in children {
            self.children.push(Box::new(child));
        }
        self
    }

    // =========================================================================
    // Conditional Builders
    // =========================================================================

    /// Conditionally apply a transformation to self.
    #[inline]
    pub fn when<F>(self, condition: bool, f: F) -> Self
    where
        F: FnOnce(Self) -> Self,
    {
        if condition {
            f(self)
        } else {
            self
        }
    }

    // =========================================================================
    // Events
    // =========================================================================

    // =========================================================================
    // Event Handlers
    // =========================================================================

    /// Register a click handler
    pub fn on_click<F>(mut self, handler: F) -> Self
    where
        F: Fn(&crate::event_handler::EventContext) + Send + Sync + 'static,
    {
        self.inner = self.inner.on_click(handler);
        self
    }

    /// Register a mouse down handler
    pub fn on_mouse_down<F>(mut self, handler: F) -> Self
    where
        F: Fn(&crate::event_handler::EventContext) + Send + Sync + 'static,
    {
        self.inner = self.inner.on_mouse_down(handler);
        self
    }

    /// Register a mouse up handler
    pub fn on_mouse_up<F>(mut self, handler: F) -> Self
    where
        F: Fn(&crate::event_handler::EventContext) + Send + Sync + 'static,
    {
        self.inner = self.inner.on_mouse_up(handler);
        self
    }

    /// Register a mouse move handler
    pub fn on_mouse_move<F>(mut self, handler: F) -> Self
    where
        F: Fn(&crate::event_handler::EventContext) + Send + Sync + 'static,
    {
        self.inner = self.inner.on_mouse_move(handler);
        self
    }

    /// Register a drag handler
    pub fn on_drag<F>(mut self, handler: F) -> Self
    where
        F: Fn(&crate::event_handler::EventContext) + Send + Sync + 'static,
    {
        self.inner = self.inner.on_drag(handler);
        self
    }

    /// Register a scroll handler
    pub fn on_scroll<F>(mut self, handler: F) -> Self
    where
        F: Fn(&crate::event_handler::EventContext) + Send + Sync + 'static,
    {
        self.inner = self.inner.on_scroll(handler);
        self
    }

    /// Register a hover enter handler
    pub fn on_hover_enter<F>(mut self, handler: F) -> Self
    where
        F: Fn(&crate::event_handler::EventContext) + Send + Sync + 'static,
    {
        self.inner = self.inner.on_hover_enter(handler);
        self
    }

    /// Register a hover leave handler
    pub fn on_hover_leave<F>(mut self, handler: F) -> Self
    where
        F: Fn(&crate::event_handler::EventContext) + Send + Sync + 'static,
    {
        self.inner = self.inner.on_hover_leave(handler);
        self
    }

    /// Register a focus handler
    pub fn on_focus<F>(mut self, handler: F) -> Self
    where
        F: Fn(&crate::event_handler::EventContext) + Send + Sync + 'static,
    {
        self.inner = self.inner.on_focus(handler);
        self
    }

    /// Register a blur handler
    pub fn on_blur<F>(mut self, handler: F) -> Self
    where
        F: Fn(&crate::event_handler::EventContext) + Send + Sync + 'static,
    {
        self.inner = self.inner.on_blur(handler);
        self
    }
    // =========================================================================
    // ID
    // =========================================================================

    /// Set element ID for selector API queries
    pub fn id(mut self, id: impl Into<String>) -> Self {
        self.element_id = Some(id.into());
        self
    }
}

impl Default for Notch {
    fn default() -> Self {
        Self::new()
    }
}

// =============================================================================
// Path Building for Complex Notchs
// =============================================================================

/// Build the path for a shape with configurable corners, scoops, bulges, cuts, and peaks
///
/// This handles convex (standard rounding), concave curves, step notches,
/// center scoops (Dynamic Island-style centered indentations),
/// center bulges (outward protrusions for active menu highlighting),
/// center cuts (sharp V-shaped inward notches), and
/// center peaks (sharp V-shaped outward points).
/// The path is built clockwise starting from the top-left corner.
///
/// For convex corners: the curve stays inside the bounds
/// For concave corners: the curve bows outward, extending beyond the bounds
/// For center scoops: the curve bows inward, staying inside the bounds
/// For center bulges: the curve protrudes outward, extending beyond the bounds
/// For center cuts: sharp V going inward
/// For center peaks: sharp V going outward
#[allow(clippy::too_many_arguments)]
fn build_shape_path(
    bounds: Rect,
    corners: &CornersConfig,
    top_center_scoop: Option<&CenterScoop>,
    bottom_center_scoop: Option<&CenterScoop>,
    top_center_bulge: Option<&CenterBulge>,
    bottom_center_bulge: Option<&CenterBulge>,
    top_center_cut: Option<&CenterCut>,
    bottom_center_cut: Option<&CenterCut>,
    top_center_peak: Option<&CenterPeak>,
    bottom_center_peak: Option<&CenterPeak>,
) -> Path {
    let w = bounds.width();
    let h = bounds.height();
    let x = bounds.x();
    let y = bounds.y();

    // Clamp radii to half the smaller dimension
    let max_radius = (w.min(h)) / 2.0;
    let clamp = |r: f32| r.min(max_radius);

    let tl = &corners.top_left;
    let tr = &corners.top_right;
    let br = &corners.bottom_right;
    let bl = &corners.bottom_left;

    let tl_r = clamp(tl.radius);
    let tr_r = clamp(tr.radius);
    let br_r = clamp(br.radius);
    let bl_r = clamp(bl.radius);

    let mut path = Path::new();

    // Determine where the top edge starts (after top-left corner)
    let top_start_x = match tl.style {
        CornerStyle::Concave => x - tl_r,              // Extends left
        CornerStyle::Convex if tl_r > 0.0 => x + tl_r, // Inset
        _ => x,
    };

    // Start at the beginning of the top edge
    path = path.move_to(top_start_x, y);

    // Top edge - with optional center scoop
    let top_end_x = match tr.style {
        CornerStyle::Concave => x + w + tr_r, // Extends right
        CornerStyle::Convex if tr_r > 0.0 => x + w - tr_r, // Inset
        _ => x + w,
    };

    // Draw top center scoop if present
    // The scoop curves DOWN into the shape, creating a visible indent
    if let Some(scoop) = top_center_scoop {
        let center_x = x + w / 2.0;
        let scoop_start_x = center_x - scoop.width / 2.0;
        let scoop_end_x = center_x + scoop.width / 2.0;
        let scoop_bottom_y = y + scoop.depth;

        // Corner radius for smooth entry/exit transitions (clamped to 1/4 of scoop width)
        let cr = scoop.corner_radius.min(scoop.width / 4.0).max(0.0);

        // Cubic bezier control point factor for circular arc approximation
        // k = 4 * (sqrt(2) - 1) / 3 ≈ 0.5522847498
        const K: f32 = 0.552_284_8;

        if cr > 0.0 {
            // With corner radius: concave (outward-bowing) entry/exit transitions
            // Creates "ear" shapes like macOS Dynamic Island
            let effective_start_x = scoop_start_x + cr;
            let effective_end_x = scoop_end_x - cr;
            let effective_start_y = y + cr;
            let effective_rx = (effective_end_x - effective_start_x) / 2.0;
            let effective_ry = scoop.depth - cr;

            // Line to entry corner point
            path = path.line_to(scoop_start_x, y);

            // Entry corner: CONCAVE ear - control toward CENTER makes fill bulge outward
            path = path.quad_to(
                scoop_start_x + cr * 0.55,
                y, // control: toward center (right)
                effective_start_x,
                effective_start_y, // end at main scoop start
            );

            // Main scoop curve: from effective start to bottom center
            path = path.cubic_to(
                effective_start_x,
                effective_start_y + K * effective_ry, // control 1
                center_x - K * effective_rx,
                scoop_bottom_y, // control 2
                center_x,
                scoop_bottom_y, // end at bottom center
            );

            // Main scoop curve: from bottom center to effective end
            path = path.cubic_to(
                center_x + K * effective_rx,
                scoop_bottom_y, // control 1
                effective_end_x,
                effective_start_y + K * effective_ry, // control 2
                effective_end_x,
                effective_start_y, // end at effective end
            );

            // Exit corner: CONCAVE ear - control toward CENTER makes fill bulge outward
            path = path.quad_to(
                scoop_end_x - cr * 0.55,
                y, // control: toward center (left)
                scoop_end_x,
                y, // end at top edge
            );

            // Line to top edge end
            path = path.line_to(top_end_x, y);
        } else {
            // Without corner radius: sharp transitions (original behavior)
            let rx = scoop.width / 2.0;
            let ry = scoop.depth;

            // Line to scoop start
            path = path.line_to(scoop_start_x, y);

            // First quarter arc: from left edge to bottom center
            path = path.cubic_to(
                scoop_start_x,
                y + K * ry, // control 1
                center_x - K * rx,
                scoop_bottom_y, // control 2
                center_x,
                scoop_bottom_y, // end at bottom center
            );

            // Second quarter arc: from bottom center to right edge
            path = path.cubic_to(
                center_x + K * rx,
                scoop_bottom_y, // control 1
                scoop_end_x,
                y + K * ry, // control 2
                scoop_end_x,
                y, // end at right edge
            );

            // Line to top edge end
            path = path.line_to(top_end_x, y);
        }
    } else if let Some(bulge) = top_center_bulge {
        // Draw top center bulge - protrudes UPWARD out of the shape
        let center_x = x + w / 2.0;
        let bulge_start_x = center_x - bulge.width / 2.0;
        let bulge_end_x = center_x + bulge.width / 2.0;
        let bulge_top_y = y - bulge.height; // Protrudes upward (negative y)

        // Corner radius for smooth entry/exit transitions
        let cr = bulge.corner_radius.min(bulge.width / 4.0).max(0.0);

        const K: f32 = 0.552_284_8;

        if cr > 0.0 {
            // With corner radius: smooth convex entry/exit transitions
            // Control points placed at the "corner" for smooth quarter-circle curves
            let effective_start_x = bulge_start_x + cr;
            let effective_end_x = bulge_end_x - cr;
            let effective_start_y = y - cr; // Going upward
            let effective_rx = (effective_end_x - effective_start_x) / 2.0;
            let effective_ry = bulge.height - cr;

            // Line to entry corner point
            path = path.line_to(bulge_start_x, y);

            // Entry corner: smooth convex quarter-circle transition
            // Control at corner where horizontal tangent from start meets vertical tangent to end
            path = path.quad_to(
                effective_start_x, // control x: at the corner (right of start)
                y,                 // control y: at edge level (horizontal tangent)
                effective_start_x,
                effective_start_y, // end at main bulge start
            );

            // Main bulge curve: from effective start to top center
            path = path.cubic_to(
                effective_start_x,
                effective_start_y - K * effective_ry, // control 1
                center_x - K * effective_rx,
                bulge_top_y, // control 2
                center_x,
                bulge_top_y, // end at top center
            );

            // Main bulge curve: from top center to effective end
            path = path.cubic_to(
                center_x + K * effective_rx,
                bulge_top_y, // control 1
                effective_end_x,
                effective_start_y - K * effective_ry, // control 2
                effective_end_x,
                effective_start_y, // end at effective end
            );

            // Exit corner: smooth convex quarter-circle transition back to edge
            // Control at corner where vertical tangent from start meets horizontal tangent to end
            path = path.quad_to(
                effective_end_x, // control x: at the corner (left of end)
                y,               // control y: at edge level (horizontal tangent)
                bulge_end_x,
                y, // end at top edge
            );

            // Line to top edge end
            path = path.line_to(top_end_x, y);
        } else {
            // Without corner radius: smooth arc with horizontal tangents at edges
            // This creates a curve that smoothly wraps around circular content
            let rx = bulge.width / 2.0;

            // Line to bulge start
            path = path.line_to(bulge_start_x, y);

            // First half arc: from left edge to top center
            // Both control points positioned for horizontal tangents:
            // - At start: tangent is horizontal (going right toward center)
            // - At end: tangent is horizontal (at the peak of the curve)
            path = path.cubic_to(
                bulge_start_x + K * rx, // control 1 x: offset right (horizontal tangent at start)
                y,                      // control 1 y: same as start y
                center_x - K * rx,      // control 2 x: offset left from center
                bulge_top_y,            // control 2 y: same as end (horizontal tangent at peak)
                center_x,
                bulge_top_y, // end at top center
            );

            // Second half arc: from top center to right edge
            // Both control points positioned for horizontal tangents
            path = path.cubic_to(
                center_x + K * rx,    // control 1 x: offset right from center
                bulge_top_y,          // control 1 y: same as start (horizontal tangent at peak)
                bulge_end_x - K * rx, // control 2 x: offset left (horizontal tangent at end)
                y,                    // control 2 y: same as end y
                bulge_end_x,
                y, // end at right edge
            );

            // Line to top edge end
            path = path.line_to(top_end_x, y);
        }
    } else if let Some(cut) = top_center_cut {
        // Draw top center V-cut - sharp V going DOWNWARD into the shape
        let center_x = x + w / 2.0;
        let cut_start_x = center_x - cut.width / 2.0;
        let cut_end_x = center_x + cut.width / 2.0;
        let cut_point_y = y + cut.depth; // Point goes downward (into shape)

        // Line to cut start
        path = path.line_to(cut_start_x, y);

        // Line down to the V point
        path = path.line_to(center_x, cut_point_y);

        // Line up to cut end
        path = path.line_to(cut_end_x, y);

        // Line to top edge end
        path = path.line_to(top_end_x, y);
    } else if let Some(peak) = top_center_peak {
        // Draw top center V-peak - sharp V pointing UPWARD out of the shape
        let center_x = x + w / 2.0;
        let peak_start_x = center_x - peak.width / 2.0;
        let peak_end_x = center_x + peak.width / 2.0;
        let peak_point_y = y - peak.height; // Point goes upward (out of shape)

        // Line to peak start
        path = path.line_to(peak_start_x, y);

        // Line up to the V point
        path = path.line_to(center_x, peak_point_y);

        // Line down to peak end
        path = path.line_to(peak_end_x, y);

        // Line to top edge end
        path = path.line_to(top_end_x, y);
    } else {
        path = path.line_to(top_end_x, y);
    }

    // Top-right corner
    match tr.style {
        CornerStyle::Step => {
            path = path.line_to(x + w + tr_r, y);
            path = path.line_to(x + w + tr_r, y + tr_r);
            path = path.line_to(x + w, y + tr_r);
        }
        CornerStyle::Concave => {
            // Concave: smooth quarter-circle from extended top to right edge
            // Control at the outer corner for proper curve
            path = path.quad_to(x + w, y, x + w, y + tr_r);
        }
        CornerStyle::Convex if tr_r > 0.0 => {
            // Convex: standard rounded corner
            path = path.quad_to(x + w, y, x + w, y + tr_r);
        }
        _ => {}
    }

    // Right edge to bottom-right corner
    let right_end_y = match br.style {
        CornerStyle::Concave => y + h + br_r, // Extends down
        CornerStyle::Convex if br_r > 0.0 => y + h - br_r, // Inset
        _ => y + h,
    };
    path = path.line_to(x + w, right_end_y);

    // Bottom-right corner
    match br.style {
        CornerStyle::Step => {
            path = path.line_to(x + w, y + h + br_r);
            path = path.line_to(x + w - br_r, y + h + br_r);
            path = path.line_to(x + w - br_r, y + h);
        }
        CornerStyle::Concave => {
            // Concave: curve inward from extended position to bottom edge
            path = path.quad_to(x + w, y + h, x + w - br_r, y + h);
        }
        CornerStyle::Convex if br_r > 0.0 => {
            // Convex: standard rounded corner
            path = path.quad_to(x + w, y + h, x + w - br_r, y + h);
        }
        _ => {}
    }

    // Bottom edge - with optional center scoop
    let bottom_end_x = match bl.style {
        CornerStyle::Concave => x - bl_r,              // Extends left
        CornerStyle::Convex if bl_r > 0.0 => x + bl_r, // Inset
        _ => x,
    };

    // Draw bottom center scoop if present
    // Path is going from right to left on bottom edge
    // The scoop curves UP into the shape, creating a visible indent
    if let Some(scoop) = bottom_center_scoop {
        let center_x = x + w / 2.0;
        let scoop_start_x = center_x + scoop.width / 2.0; // Start from right side (path goes right to left)
        let scoop_end_x = center_x - scoop.width / 2.0; // End at left side
        let scoop_top_y = y + h - scoop.depth;

        // Corner radius for smooth entry/exit transitions (clamped to 1/4 of scoop width)
        let cr = scoop.corner_radius.min(scoop.width / 4.0).max(0.0);

        // Cubic bezier control point factor for circular arc approximation
        const K: f32 = 0.552_284_8;

        if cr > 0.0 {
            // With corner radius: concave (outward-bowing) entry/exit transitions
            // Note: path goes right to left on bottom edge
            let effective_start_x = scoop_start_x - cr; // Inset from right
            let effective_end_x = scoop_end_x + cr; // Inset from left
            let effective_start_y = y + h - cr;
            let effective_rx = (effective_start_x - effective_end_x) / 2.0;
            let effective_ry = scoop.depth - cr;

            // Line to entry corner point (from right)
            path = path.line_to(scoop_start_x, y + h);

            // Entry corner: CONCAVE ear - control toward CENTER makes fill bulge outward
            // (path goes right to left on bottom edge)
            path = path.quad_to(
                scoop_start_x - cr * 0.55,
                y + h, // control: toward center (left)
                effective_start_x,
                effective_start_y, // end at main scoop start
            );

            // Main scoop curve: from effective start to top center
            path = path.cubic_to(
                effective_start_x,
                effective_start_y - K * effective_ry, // control 1
                center_x + K * effective_rx,
                scoop_top_y, // control 2
                center_x,
                scoop_top_y, // end at top center
            );

            // Main scoop curve: from top center to effective end
            path = path.cubic_to(
                center_x - K * effective_rx,
                scoop_top_y, // control 1
                effective_end_x,
                effective_start_y - K * effective_ry, // control 2
                effective_end_x,
                effective_start_y, // end at effective end
            );

            // Exit corner: CONCAVE ear - control toward CENTER makes fill bulge outward
            path = path.quad_to(
                scoop_end_x + cr * 0.55,
                y + h, // control: toward center (right)
                scoop_end_x,
                y + h, // end at bottom edge
            );

            // Line to bottom edge end
            path = path.line_to(bottom_end_x, y + h);
        } else {
            // Without corner radius: sharp transitions (original behavior)
            let rx = scoop.width / 2.0;
            let ry = scoop.depth;

            // Line to scoop start (from right)
            path = path.line_to(scoop_start_x, y + h);

            // First quarter arc: from right edge to top center (going right to left)
            path = path.cubic_to(
                scoop_start_x,
                y + h - K * ry, // control 1
                center_x + K * rx,
                scoop_top_y, // control 2
                center_x,
                scoop_top_y, // end at top center
            );

            // Second quarter arc: from top center to left edge
            path = path.cubic_to(
                center_x - K * rx,
                scoop_top_y, // control 1
                scoop_end_x,
                y + h - K * ry, // control 2
                scoop_end_x,
                y + h, // end at left edge
            );

            // Line to bottom edge end
            path = path.line_to(bottom_end_x, y + h);
        }
    } else if let Some(bulge) = bottom_center_bulge {
        // Draw bottom center bulge - protrudes DOWNWARD out of the shape
        // Path is going from right to left on bottom edge
        let center_x = x + w / 2.0;
        let bulge_start_x = center_x + bulge.width / 2.0; // Start from right side
        let bulge_end_x = center_x - bulge.width / 2.0; // End at left side
        let bulge_bottom_y = y + h + bulge.height; // Protrudes downward (positive y)

        // Corner radius for smooth entry/exit transitions
        let cr = bulge.corner_radius.min(bulge.width / 4.0).max(0.0);

        const K: f32 = 0.552_284_8;

        if cr > 0.0 {
            // With corner radius: smooth convex entry/exit transitions
            // Control points placed at the "corner" for smooth quarter-circle curves
            let effective_start_x = bulge_start_x - cr; // Inset from right
            let effective_end_x = bulge_end_x + cr; // Inset from left
            let effective_start_y = y + h + cr; // Going downward
            let effective_rx = (effective_start_x - effective_end_x) / 2.0;
            let effective_ry = bulge.height - cr;

            // Line to entry corner point (from right)
            path = path.line_to(bulge_start_x, y + h);

            // Entry corner: smooth convex quarter-circle transition going downward
            // Control at corner where horizontal tangent from start meets vertical tangent to end
            path = path.quad_to(
                effective_start_x, // control x: at the corner (left of start, path goes right-to-left)
                y + h,             // control y: at edge level (horizontal tangent)
                effective_start_x,
                effective_start_y, // end at main bulge start
            );

            // Main bulge curve: from effective start to bottom center
            path = path.cubic_to(
                effective_start_x,
                effective_start_y + K * effective_ry, // control 1
                center_x + K * effective_rx,
                bulge_bottom_y, // control 2
                center_x,
                bulge_bottom_y, // end at bottom center
            );

            // Main bulge curve: from bottom center to effective end
            path = path.cubic_to(
                center_x - K * effective_rx,
                bulge_bottom_y, // control 1
                effective_end_x,
                effective_start_y + K * effective_ry, // control 2
                effective_end_x,
                effective_start_y, // end at effective end
            );

            // Exit corner: smooth convex quarter-circle transition back to edge
            // Control at corner where vertical tangent from start meets horizontal tangent to end
            path = path.quad_to(
                effective_end_x, // control x: at the corner (right of end)
                y + h,           // control y: at edge level (horizontal tangent)
                bulge_end_x,
                y + h, // end at bottom edge
            );

            // Line to bottom edge end
            path = path.line_to(bottom_end_x, y + h);
        } else {
            // Without corner radius: smooth arc with horizontal tangents at edges
            // Path goes right-to-left on bottom edge
            let rx = bulge.width / 2.0;

            // Line to bulge start (from right)
            path = path.line_to(bulge_start_x, y + h);

            // First half arc: from right edge to bottom center
            // Both control points positioned for horizontal tangents
            path = path.cubic_to(
                bulge_start_x - K * rx, // control 1 x: offset left (horizontal tangent at start)
                y + h,                  // control 1 y: same as start y
                center_x + K * rx,      // control 2 x: offset right from center
                bulge_bottom_y,         // control 2 y: same as end (horizontal tangent at peak)
                center_x,
                bulge_bottom_y, // end at bottom center
            );

            // Second half arc: from bottom center to left edge
            // Both control points positioned for horizontal tangents
            path = path.cubic_to(
                center_x - K * rx,    // control 1 x: offset left from center
                bulge_bottom_y,       // control 1 y: same as start (horizontal tangent at peak)
                bulge_end_x + K * rx, // control 2 x: offset right (horizontal tangent at end)
                y + h,                // control 2 y: same as end y
                bulge_end_x,
                y + h, // end at left edge
            );

            // Line to bottom edge end
            path = path.line_to(bottom_end_x, y + h);
        }
    } else if let Some(cut) = bottom_center_cut {
        // Draw bottom center V-cut - sharp V going UPWARD into the shape
        // Path goes right-to-left on bottom edge
        let center_x = x + w / 2.0;
        let cut_start_x = center_x + cut.width / 2.0; // Start from right
        let cut_end_x = center_x - cut.width / 2.0; // End at left
        let cut_point_y = y + h - cut.depth; // Point goes upward (into shape)

        // Line to cut start (from right)
        path = path.line_to(cut_start_x, y + h);

        // Line up to the V point
        path = path.line_to(center_x, cut_point_y);

        // Line down to cut end
        path = path.line_to(cut_end_x, y + h);

        // Line to bottom edge end
        path = path.line_to(bottom_end_x, y + h);
    } else if let Some(peak) = bottom_center_peak {
        // Draw bottom center V-peak - sharp V pointing DOWNWARD out of the shape
        // Path goes right-to-left on bottom edge
        let center_x = x + w / 2.0;
        let peak_start_x = center_x + peak.width / 2.0; // Start from right
        let peak_end_x = center_x - peak.width / 2.0; // End at left
        let peak_point_y = y + h + peak.height; // Point goes downward (out of shape)

        // Line to peak start (from right)
        path = path.line_to(peak_start_x, y + h);

        // Line down to the V point
        path = path.line_to(center_x, peak_point_y);

        // Line up to peak end
        path = path.line_to(peak_end_x, y + h);

        // Line to bottom edge end
        path = path.line_to(bottom_end_x, y + h);
    } else {
        path = path.line_to(bottom_end_x, y + h);
    }

    // Bottom-left corner
    match bl.style {
        CornerStyle::Step => {
            path = path.line_to(x - bl_r, y + h);
            path = path.line_to(x - bl_r, y + h - bl_r);
            path = path.line_to(x, y + h - bl_r);
        }
        CornerStyle::Concave => {
            // Concave: curve inward from extended position to left edge
            path = path.quad_to(x, y + h, x, y + h - bl_r);
        }
        CornerStyle::Convex if bl_r > 0.0 => {
            // Convex: standard rounded corner
            path = path.quad_to(x, y + h, x, y + h - bl_r);
        }
        _ => {}
    }

    // Left edge back to top-left corner
    let left_end_y = match tl.style {
        CornerStyle::Concave => y + tl_r, // Stop before top, curve will bow outward
        CornerStyle::Convex if tl_r > 0.0 => y + tl_r, // Inset
        _ => y,
    };
    path = path.line_to(x, left_end_y);

    // Top-left corner (completes the shape)
    match tl.style {
        CornerStyle::Step => {
            path = path.line_to(x, y - tl_r);
            path = path.line_to(x - tl_r, y - tl_r);
            path = path.line_to(x - tl_r, y);
        }
        CornerStyle::Concave => {
            // Concave: smooth quarter-circle from left edge to extended top
            // Control at the inner corner for proper curve
            path = path.quad_to(x, y, top_start_x, y);
        }
        CornerStyle::Convex if tl_r > 0.0 => {
            // Convex: standard rounded corner back to start
            path = path.quad_to(x, y, top_start_x, y);
        }
        _ => {}
    }

    path.close()
}

// =============================================================================
// Path Shadow Utilities
// =============================================================================

/// Offset a path outward by a given amount.
///
/// This creates an expanded version of the path for shadow rendering.
/// The offset is applied by scaling the path from its center.
fn offset_path_by(path: &Path, amount: f32, bounds: Rect) -> Path {
    use blinc_core::PathCommand;

    if amount <= 0.0 {
        return path.clone();
    }

    // Calculate the center of the bounds
    let cx = bounds.x() + bounds.width() / 2.0;
    let cy = bounds.y() + bounds.height() / 2.0;

    // Calculate scale factor based on offset amount
    let scale_x = if bounds.width() > 0.0 {
        (bounds.width() + amount * 2.0) / bounds.width()
    } else {
        1.0
    };
    let scale_y = if bounds.height() > 0.0 {
        (bounds.height() + amount * 2.0) / bounds.height()
    } else {
        1.0
    };

    // Transform each point by scaling from center
    let transform_point = |x: f32, y: f32| -> (f32, f32) {
        let new_x = cx + (x - cx) * scale_x;
        let new_y = cy + (y - cy) * scale_y;
        (new_x, new_y)
    };

    // Build new path with transformed commands
    let mut new_path = Path::new();
    for cmd in path.commands() {
        match cmd {
            PathCommand::MoveTo(p) => {
                let (x, y) = transform_point(p.x, p.y);
                new_path = new_path.move_to(x, y);
            }
            PathCommand::LineTo(p) => {
                let (x, y) = transform_point(p.x, p.y);
                new_path = new_path.line_to(x, y);
            }
            PathCommand::QuadTo { control, end } => {
                let (cx, cy) = transform_point(control.x, control.y);
                let (ex, ey) = transform_point(end.x, end.y);
                new_path = new_path.quad_to(cx, cy, ex, ey);
            }
            PathCommand::CubicTo {
                control1,
                control2,
                end,
            } => {
                let (c1x, c1y) = transform_point(control1.x, control1.y);
                let (c2x, c2y) = transform_point(control2.x, control2.y);
                let (ex, ey) = transform_point(end.x, end.y);
                new_path = new_path.cubic_to(c1x, c1y, c2x, c2y, ex, ey);
            }
            PathCommand::ArcTo {
                radii,
                rotation,
                large_arc,
                sweep,
                end,
            } => {
                let (ex, ey) = transform_point(end.x, end.y);
                // Scale radii proportionally
                let new_radii = blinc_core::Vec2::new(radii.x * scale_x, radii.y * scale_y);
                new_path = new_path.arc_to(new_radii, *rotation, *large_arc, *sweep, ex, ey);
            }
            PathCommand::Close => {
                new_path = new_path.close();
            }
        }
    }

    new_path
}

/// Draw a path-based shadow using multi-layer blur approximation.
///
/// This renders multiple offset versions of the path with decreasing opacity
/// to simulate a soft shadow effect.
fn draw_path_shadow(ctx: &mut dyn DrawContext, path: &Path, bounds: Rect, shadow: &Shadow) {
    let blur = shadow.blur;
    if blur <= 0.0 {
        return;
    }

    // More layers = smoother shadow (aim for ~2px per layer)
    let layers = ((blur / 1.5).ceil() as usize).clamp(8, 24);

    // Apply shadow offset by translating
    let offset_x = shadow.offset_x;
    let offset_y = shadow.offset_y;

    // Draw layers from outermost to innermost
    for i in (0..layers).rev() {
        let t = (i as f32 + 0.5) / layers as f32;
        let layer_offset = (blur + shadow.spread) * t;

        // Gaussian-like alpha falloff: exp(-t^2 * k)
        // This creates a smoother, more natural shadow gradient
        let gaussian = (-t * t * 3.0).exp();
        let alpha = shadow.color.a * gaussian * 0.15;
        if alpha < 0.005 {
            continue;
        }

        // Create offset path
        let offset_path = offset_path_by(path, layer_offset, bounds);

        // Apply shadow offset via transform
        ctx.push_transform(Transform::translate(offset_x, offset_y));

        // Draw the shadow layer
        let shadow_color = Color::rgba(shadow.color.r, shadow.color.g, shadow.color.b, alpha);
        ctx.fill_path(&offset_path, Brush::Solid(shadow_color));

        ctx.pop_transform();
    }
}

// =============================================================================
// Notch Render Data
// =============================================================================

/// Data for rendering a shape element
#[derive(Clone)]
pub struct NotchRenderData {
    pub corners: CornersConfig,
    pub background: Option<Brush>,
    pub border_color: Option<Color>,
    pub border_width: f32,
    pub shadow: Option<Shadow>,
}

impl std::fmt::Debug for NotchRenderData {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("NotchRenderData")
            .field("has_concave_curves", &self.corners.has_concave_curves())
            .finish()
    }
}

// =============================================================================
// ElementBuilder Implementation
// =============================================================================

impl ElementBuilder for Notch {
    fn build(&self, tree: &mut LayoutTree) -> LayoutNodeId {
        // Clone style and adjust padding for center scoops
        // Scoops curve INWARD, so we need to reserve that space for children
        let mut style = self.style.clone();

        // Add top padding for top center scoop
        if let Some(scoop) = &self.top_center_scoop {
            let current_top = match style.padding.top {
                LengthPercentage::Length(v) => v,
                LengthPercentage::Percent(_) => 0.0, // Can't add to percentage
            };
            style.padding.top = LengthPercentage::Length(current_top + scoop.depth);
        }

        // Add bottom padding for bottom center scoop
        if let Some(scoop) = &self.bottom_center_scoop {
            let current_bottom = match style.padding.bottom {
                LengthPercentage::Length(v) => v,
                LengthPercentage::Percent(_) => 0.0, // Can't add to percentage
            };
            style.padding.bottom = LengthPercentage::Length(current_bottom + scoop.depth);
        }

        // Create the layout node with adjusted style
        let node = tree.create_node(style);

        // Build and add children
        for child in &self.children {
            let child_node = child.build(tree);
            tree.add_child(node, child_node);
        }

        node
    }

    fn render_props(&self) -> RenderProps {
        // If we have concave curves, step corners, center scoops, bulges, cuts, or peaks,
        // we need custom canvas rendering. Otherwise use standard div rendering.
        let has_center_scoop =
            self.top_center_scoop.is_some() || self.bottom_center_scoop.is_some();
        let has_center_bulge =
            self.top_center_bulge.is_some() || self.bottom_center_bulge.is_some();
        let has_center_cut = self.top_center_cut.is_some() || self.bottom_center_cut.is_some();
        let has_center_peak = self.top_center_peak.is_some() || self.bottom_center_peak.is_some();
        let needs_custom = self.corners.needs_custom_rendering()
            || has_center_scoop
            || has_center_bulge
            || has_center_cut
            || has_center_peak;

        if needs_custom {
            // For shapes with custom corners/scoops, we'll set background to None here
            // and render via canvas. The canvas data is passed separately.
            RenderProps {
                background: None, // Rendered via canvas
                border_radius: CornerRadius::ZERO,
                border_color: None,
                border_width: 0.0,
                border_sides: Default::default(),
                layer: self.render_layer,
                material: self.material.clone(),
                shadow: None, // Rendered via canvas
                transform: None,
                opacity: self.opacity,
                ..Default::default()
            }
        } else {
            // Standard rendering path - use div-like rendering
            RenderProps {
                background: self.background.clone(),
                border_radius: self.corners.to_corner_radius(),
                border_color: self.border_color,
                border_width: self.border_width,
                border_sides: Default::default(),
                layer: self.render_layer,
                material: self.material.clone(),
                shadow: self.shadow,
                transform: None,
                opacity: self.opacity,
                ..Default::default()
            }
        }
    }

    fn children_builders(&self) -> &[Box<dyn ElementBuilder>] {
        &self.children
    }

    fn element_type_id(&self) -> ElementTypeId {
        // If we have custom corners, center scoops, bulges, cuts, or peaks, use Canvas type
        // Otherwise, use Div for standard rendering
        let has_center_scoop =
            self.top_center_scoop.is_some() || self.bottom_center_scoop.is_some();
        let has_center_bulge =
            self.top_center_bulge.is_some() || self.bottom_center_bulge.is_some();
        let has_center_cut = self.top_center_cut.is_some() || self.bottom_center_cut.is_some();
        let has_center_peak = self.top_center_peak.is_some() || self.bottom_center_peak.is_some();
        let needs_custom = self.corners.needs_custom_rendering()
            || has_center_scoop
            || has_center_bulge
            || has_center_cut
            || has_center_peak;
        if needs_custom {
            ElementTypeId::Canvas
        } else {
            ElementTypeId::Div
        }
    }

    fn layout_style(&self) -> Option<&taffy::Style> {
        Some(&self.style)
    }

    fn canvas_render_info(&self) -> Option<CanvasRenderFn> {
        // Only provide canvas render if we have custom corners, scoops, bulges, cuts, or peaks
        let has_center_scoop =
            self.top_center_scoop.is_some() || self.bottom_center_scoop.is_some();
        let has_center_bulge =
            self.top_center_bulge.is_some() || self.bottom_center_bulge.is_some();
        let has_center_cut = self.top_center_cut.is_some() || self.bottom_center_cut.is_some();
        let has_center_peak = self.top_center_peak.is_some() || self.bottom_center_peak.is_some();
        let needs_custom = self.corners.needs_custom_rendering()
            || has_center_scoop
            || has_center_bulge
            || has_center_cut
            || has_center_peak;
        if !needs_custom {
            return None;
        }

        let corners = self.corners;
        let top_center_scoop = self.top_center_scoop;
        let bottom_center_scoop = self.bottom_center_scoop;
        let top_center_bulge = self.top_center_bulge;
        let bottom_center_bulge = self.bottom_center_bulge;
        let top_center_cut = self.top_center_cut;
        let bottom_center_cut = self.bottom_center_cut;
        let top_center_peak = self.top_center_peak;
        let bottom_center_peak = self.bottom_center_peak;
        let background = self.background.clone();
        let border_color = self.border_color;
        let border_width = self.border_width;
        let shadow = self.shadow;
        let opacity = self.opacity;

        Some(Rc::new(
            move |ctx: &mut dyn DrawContext, bounds: CanvasBounds| {
                // For concave corners, we need to offset the rect inward so the concave
                // portions (which extend outward) stay within the canvas bounds
                let tl_r = corners.top_left.radius;
                let tr_r = corners.top_right.radius;
                let bl_r = corners.bottom_left.radius;
                let br_r = corners.bottom_right.radius;

                // Calculate offsets for concave corners
                let left_offset =
                    if corners.top_left.is_concave() || corners.bottom_left.is_concave() {
                        tl_r.max(bl_r)
                    } else {
                        0.0
                    };
                let right_offset =
                    if corners.top_right.is_concave() || corners.bottom_right.is_concave() {
                        tr_r.max(br_r)
                    } else {
                        0.0
                    };
                // Top offset: concave corners extend upward, bulges and peaks also protrude upward
                let top_offset = {
                    let corner_offset =
                        if corners.top_left.is_concave() || corners.top_right.is_concave() {
                            tl_r.max(tr_r)
                        } else {
                            0.0
                        };
                    let bulge_offset = top_center_bulge.map(|b| b.height).unwrap_or(0.0);
                    let peak_offset = top_center_peak.map(|p| p.height).unwrap_or(0.0);
                    corner_offset.max(bulge_offset).max(peak_offset)
                };

                // Bottom offset: concave corners extend downward, bulges and peaks also protrude downward
                let bottom_offset = {
                    let corner_offset =
                        if corners.bottom_left.is_concave() || corners.bottom_right.is_concave() {
                            bl_r.max(br_r)
                        } else {
                            0.0
                        };
                    let bulge_offset = bottom_center_bulge.map(|b| b.height).unwrap_or(0.0);
                    let peak_offset = bottom_center_peak.map(|p| p.height).unwrap_or(0.0);
                    corner_offset.max(bulge_offset).max(peak_offset)
                };

                // NOTE: Center scoops and cuts do NOT add to the rect offset - they go INWARD.
                // Bulges and peaks DO protrude outward and need offset space.

                // Create the rect with offsets so concave curves, bulges, and peaks stay within bounds
                let rect = Rect::new(
                    left_offset,
                    top_offset,
                    bounds.width - left_offset - right_offset,
                    bounds.height - top_offset - bottom_offset,
                );

                // Build the path for any combination of corner types, scoops, bulges, cuts, and peaks
                let path = build_shape_path(
                    rect,
                    &corners,
                    top_center_scoop.as_ref(),
                    bottom_center_scoop.as_ref(),
                    top_center_bulge.as_ref(),
                    bottom_center_bulge.as_ref(),
                    top_center_cut.as_ref(),
                    bottom_center_cut.as_ref(),
                    top_center_peak.as_ref(),
                    bottom_center_peak.as_ref(),
                );

                // Draw shadow first (behind the fill)
                if let Some(shadow) = shadow {
                    // Use path-based shadow for accurate curved shadow rendering
                    draw_path_shadow(ctx, &path, rect, &shadow);
                }

                // Fill the path (with opacity applied)
                if let Some(ref brush) = background {
                    let brush_with_opacity = if opacity < 1.0 {
                        match brush.clone() {
                            Brush::Solid(color) => {
                                Brush::Solid(color.with_alpha(color.a * opacity))
                            }
                            Brush::Gradient(g) => {
                                // Apply opacity to all gradient stops
                                let new_stops: Vec<_> = g
                                    .stops()
                                    .iter()
                                    .map(|stop| {
                                        blinc_core::GradientStop::new(
                                            stop.offset,
                                            stop.color.with_alpha(stop.color.a * opacity),
                                        )
                                    })
                                    .collect();
                                // Recreate gradient with modified stops
                                let new_gradient = match g {
                                    Gradient::Linear {
                                        start,
                                        end,
                                        space,
                                        spread,
                                        ..
                                    } => Gradient::Linear {
                                        start,
                                        end,
                                        stops: new_stops,
                                        space,
                                        spread,
                                    },
                                    Gradient::Radial {
                                        center,
                                        radius,
                                        focal,
                                        space,
                                        spread,
                                        ..
                                    } => Gradient::Radial {
                                        center,
                                        radius,
                                        focal,
                                        stops: new_stops,
                                        space,
                                        spread,
                                    },
                                    Gradient::Conic {
                                        center,
                                        start_angle,
                                        space,
                                        ..
                                    } => Gradient::Conic {
                                        center,
                                        start_angle,
                                        stops: new_stops,
                                        space,
                                    },
                                };
                                Brush::Gradient(new_gradient)
                            }
                            other => other,
                        }
                    } else {
                        brush.clone()
                    };
                    ctx.fill_path(&path, brush_with_opacity);
                }

                // Stroke the path (with opacity applied)
                if let (Some(color), width) = (border_color, border_width) {
                    if width > 0.0 {
                        let stroke_color = if opacity < 1.0 {
                            color.with_alpha(color.a * opacity)
                        } else {
                            color
                        };
                        ctx.stroke_path(
                            &path,
                            &blinc_core::Stroke::new(width),
                            Brush::Solid(stroke_color),
                        );
                    }
                }
            },
        ))
    }

    fn event_handlers(&self) -> Option<&EventHandlers> {
        if !self.event_handlers.is_empty() {
            Some(&self.event_handlers)
        } else {
            None
        }
    }
}

// =============================================================================
// Factory Function
// =============================================================================

/// Create a new notch element
///
/// Use `notch()` when you need concave (outward-bowing) curves
/// that `div()` cannot do.
///
/// # Example
///
/// ```ignore
/// use blinc_layout::prelude::*;
///
/// // Menu bar dropdown with notched connection
/// notch()
///     .concave_top(24.0)    // The notch!
///     .rounded_bottom(16.0) // Standard rounding
///     .bg(Color::BLACK)
///     .p(16.0)
///     .child(text("Battery | 87% Charged"))
/// ```
///
/// # Animated Morphing
///
/// Use signed radius for smooth animation between concave and convex:
///
/// ```ignore
/// stateful(|ctx| {
///     // Negative = concave, positive = convex
///     let top_r = ctx.spring("top", if open { -24.0 } else { 16.0 });
///     
///     notch()
///         .corner_top(top_r)   // Animates through 0 (sharp)
///         .corner_bottom(16.0)
///         .bg(Color::BLACK)
/// })
/// ```
pub fn notch() -> Notch {
    Notch::new()
}

// =============================================================================
// Tests
// =============================================================================

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_notch_no_concave_curves() {
        let s = notch().rounded(8.0);
        assert!(!s.corners.has_concave_curves());
    }

    #[test]
    fn test_notch_with_concave_curves() {
        let s = notch().concave_top(24.0);
        assert!(s.corners.has_concave_curves());
        assert!(s.corners.top_left.is_concave());
        assert!(s.corners.top_right.is_concave());
        assert!(!s.corners.bottom_left.is_concave());
        assert!(!s.corners.bottom_right.is_concave());
    }

    #[test]
    fn test_notched_dropdown() {
        let s = notch().concave_top(24.0).rounded_bottom(16.0);

        assert!(s.corners.top_left.is_concave());
        assert_eq!(s.corners.top_left.radius, 24.0);
        assert!(s.corners.top_right.is_concave());
        assert_eq!(s.corners.top_right.radius, 24.0);

        assert!(!s.corners.bottom_left.is_concave());
        assert_eq!(s.corners.bottom_left.radius, 16.0);
        assert!(!s.corners.bottom_right.is_concave());
        assert_eq!(s.corners.bottom_right.radius, 16.0);
    }

    #[test]
    fn test_element_type_changes() {
        // Without custom corners -> Div
        let s1 = notch().rounded(8.0);
        assert_eq!(s1.element_type_id(), ElementTypeId::Div);

        // With concave curves -> Canvas
        let s2 = notch().concave_top(24.0);
        assert_eq!(s2.element_type_id(), ElementTypeId::Canvas);

        // With step corners -> Canvas
        let s3 = notch().step_top(20.0);
        assert_eq!(s3.element_type_id(), ElementTypeId::Canvas);
    }

    #[test]
    fn test_signed_radius_positive() {
        // Positive = convex (standard rounding)
        let s = notch().corner_top(16.0);
        assert!(!s.corners.top_left.is_concave());
        assert!(!s.corners.top_right.is_concave());
        assert_eq!(s.corners.top_left.radius, 16.0);
        assert_eq!(s.corners.top_right.radius, 16.0);
    }

    #[test]
    fn test_signed_radius_negative() {
        // Negative = concave
        let s = notch().corner_top(-24.0);
        assert!(s.corners.top_left.is_concave());
        assert!(s.corners.top_right.is_concave());
        assert_eq!(s.corners.top_left.radius, 24.0); // Stored as positive
        assert_eq!(s.corners.top_right.radius, 24.0);
    }

    #[test]
    fn test_signed_radius_zero() {
        // Zero = sharp corner
        let s = notch().corner_top(0.0);
        assert!(!s.corners.top_left.is_concave());
        assert_eq!(s.corners.top_left.radius, 0.0);
    }

    #[test]
    fn test_signed_radius_mixed() {
        // Typical dropdown: concave top, convex bottom
        let s = notch().corner_top(-24.0).corner_bottom(16.0);

        assert!(s.corners.top_left.is_concave());
        assert!(s.corners.top_right.is_concave());
        assert!(!s.corners.bottom_left.is_concave());
        assert!(!s.corners.bottom_right.is_concave());

        assert_eq!(s.corners.top_left.radius, 24.0);
        assert_eq!(s.corners.bottom_left.radius, 16.0);
    }

    #[test]
    fn test_signed_radius_individual_corners() {
        let s = notch()
            .corner_tl(-10.0)
            .corner_tr(20.0)
            .corner_br(-30.0)
            .corner_bl(40.0);

        assert!(s.corners.top_left.is_concave());
        assert_eq!(s.corners.top_left.radius, 10.0);

        assert!(!s.corners.top_right.is_concave());
        assert_eq!(s.corners.top_right.radius, 20.0);

        assert!(s.corners.bottom_right.is_concave());
        assert_eq!(s.corners.bottom_right.radius, 30.0);

        assert!(!s.corners.bottom_left.is_concave());
        assert_eq!(s.corners.bottom_left.radius, 40.0);
    }

    #[test]
    fn test_step_corners() {
        let s = notch().step_top(20.0);
        assert!(s.corners.top_left.is_step());
        assert!(s.corners.top_right.is_step());
        assert!(!s.corners.bottom_left.is_step());
        assert!(!s.corners.bottom_right.is_step());
        assert_eq!(s.corners.top_left.radius, 20.0);
    }

    #[test]
    fn test_step_with_rounded_bottom() {
        // Sharp step at top, rounded at bottom
        let s = notch().step_top(24.0).rounded_bottom(12.0);

        assert!(s.corners.top_left.is_step());
        assert!(s.corners.top_right.is_step());
        assert!(!s.corners.bottom_left.is_step());
        assert!(!s.corners.bottom_right.is_step());

        assert_eq!(s.corners.top_left.radius, 24.0);
        assert_eq!(s.corners.bottom_left.radius, 12.0);
    }

    #[test]
    fn test_needs_custom_rendering() {
        // Convex only -> no custom rendering
        let s1 = notch().rounded(8.0);
        assert!(!s1.corners.needs_custom_rendering());

        // Concave -> custom rendering
        let s2 = notch().concave_top(24.0);
        assert!(s2.corners.needs_custom_rendering());

        // Step -> custom rendering
        let s3 = notch().step_top(20.0);
        assert!(s3.corners.needs_custom_rendering());
    }
}