/* ============================================================
   sections.css — everything below the fold
   Preston Thoman · Palm Springs portfolio
   ============================================================ */

/* ---------- shared section furniture ---------------------- */
/* Adjacent sections each contribute padding, so the gap between two of
   them is double this. Kept modest to avoid dead bands of background
   between one block of content and the next. */
.section {
  position: relative;
  padding-block: clamp(3.25rem, 6.5vh, 5rem);
  overflow: hidden;
}
/* ---------- section grounds ----------------------------------
   Each section is printed on its own ground, drawn from the place and
   from what the section is about — never a bare fill, never a blend.
   All of them are static backgrounds: painted once, nothing animates,
   nothing is promoted to a layer. Every one shares the paper grain.

     About   topographic contours of the San Jacintos — where he's from
     Path    a mid-century breeze-block screen, held to the left column
     Work    a drafting grid — the sheet things are designed on
     Skills  a fine screen-print hatch
   ---------------------------------------------------------------- */
.section--origin {
  background:
    var(--grain),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1400 900'%3E%3Cg fill='none' stroke='%23a9a2cf' stroke-opacity='.11' stroke-width='1.2'%3E%3Cpath d='M363 260 L362 264 L358 268 L351 270 L345 272 L341 274 L338 277 L336 280 L334 282 L330 284 L325 285 L321 286 L317 287 L313 288 L309 290 L305 291 L300 291 L296 290 L291 290 L286 290 L280 292 L272 294 L265 294 L260 292 L257 288 L255 284 L255 280 L255 276 L255 272 L256 269 L258 265 L261 263 L263 260 L263 258 L260 255 L256 251 L253 247 L253 243 L255 240 L259 238 L263 236 L267 233 L270 230 L274 228 L279 226 L284 226 L290 226 L295 226 L300 225 L305 225 L311 225 L315 228 L318 232 L320 236 L321 239 L323 241 L327 242 L333 242 L338 243 L343 245 L347 247 L352 250 L356 253 L361 256Z'/%3E%3Cpath d='M416 260 L414 267 L405 274 L393 279 L382 282 L375 286 L370 291 L367 296 L363 301 L356 304 L347 306 L339 307 L331 309 L324 313 L317 316 L309 317 L300 317 L292 316 L283 315 L274 317 L262 320 L248 323 L235 323 L226 320 L220 312 L218 304 L218 296 L218 289 L218 282 L220 276 L224 270 L229 265 L231 260 L230 255 L224 250 L216 243 L211 236 L211 229 L217 223 L224 219 L232 215 L238 211 L244 205 L251 200 L260 197 L271 196 L281 197 L290 197 L300 196 L310 195 L319 196 L327 201 L333 208 L336 216 L338 223 L343 226 L351 227 L361 227 L372 228 L381 232 L389 236 L397 241 L405 246 L413 253Z'/%3E%3Cpath d='M469 260 L465 271 L451 280 L434 287 L418 292 L408 298 L403 305 L398 313 L392 320 L381 325 L369 327 L356 329 L345 332 L335 337 L325 341 L313 344 L300 343 L288 342 L275 342 L261 345 L243 350 L224 354 L205 353 L191 347 L184 336 L182 323 L183 311 L183 301 L183 292 L185 283 L190 274 L196 267 L199 260 L196 253 L186 245 L175 235 L169 224 L170 214 L178 207 L190 201 L201 195 L210 188 L219 180 L229 173 L242 168 L257 167 L272 167 L286 167 L300 166 L314 166 L328 168 L339 175 L347 186 L351 198 L355 206 L363 210 L375 211 L391 211 L407 213 L421 218 L432 224 L443 231 L455 240 L465 249Z'/%3E%3Cpath d='M522 260 L514 274 L496 286 L473 294 L453 302 L441 309 L435 319 L430 330 L421 339 L407 345 L390 348 L374 351 L360 355 L347 361 L332 367 L317 370 L300 370 L284 369 L267 369 L248 373 L224 380 L198 385 L174 383 L157 374 L149 359 L148 342 L149 326 L149 313 L149 301 L151 290 L157 279 L164 269 L167 260 L161 251 L148 240 L133 227 L125 212 L128 200 L139 190 L155 182 L170 175 L182 165 L193 155 L207 145 L224 139 L243 138 L263 138 L282 138 L300 137 L319 137 L336 140 L351 150 L360 165 L366 179 L372 190 L383 194 L400 194 L422 194 L444 197 L462 203 L477 212 L491 222 L505 233 L517 246Z'/%3E%3Cpath d='M573 260 L562 277 L539 291 L511 302 L487 311 L473 321 L467 333 L461 347 L450 358 L432 366 L411 369 L391 372 L374 377 L358 385 L340 392 L321 397 L300 397 L280 396 L259 397 L234 403 L205 411 L173 416 L144 413 L124 401 L115 381 L114 360 L116 341 L116 324 L116 310 L118 296 L124 283 L131 271 L133 260 L124 249 L107 235 L90 218 L81 200 L85 185 L101 173 L121 164 L139 154 L153 143 L167 129 L184 118 L206 110 L230 108 L254 109 L277 110 L300 108 L323 108 L344 114 L362 126 L373 144 L380 162 L388 173 L403 178 L426 177 L454 177 L482 180 L504 188 L523 199 L539 212 L556 227 L569 243Z'/%3E%3Cpath d='M623 260 L609 280 L581 297 L548 309 L521 320 L506 332 L499 347 L493 364 L479 378 L458 386 L433 390 L409 394 L388 399 L369 409 L348 418 L325 424 L300 424 L275 424 L250 426 L220 433 L185 443 L147 448 L113 443 L91 427 L82 403 L82 377 L85 354 L85 335 L85 319 L86 303 L92 287 L98 273 L98 260 L87 246 L66 229 L45 209 L36 188 L43 170 L62 156 L87 145 L108 134 L125 120 L141 104 L161 90 L187 81 L216 79 L245 80 L273 81 L300 80 L327 81 L352 88 L372 103 L386 124 L394 144 L405 157 L424 161 L453 160 L488 159 L521 163 L548 173 L570 187 L589 203 L607 220 L621 239Z'/%3E%3Cpath d='M672 260 L654 283 L621 302 L583 316 L554 329 L538 343 L531 361 L524 381 L509 397 L484 407 L454 411 L426 415 L402 422 L380 433 L356 444 L329 451 L300 452 L271 452 L241 455 L206 464 L164 475 L120 480 L83 473 L59 453 L50 424 L52 394 L55 367 L56 346 L54 327 L54 309 L59 291 L65 275 L62 260 L47 244 L23 224 L-0 200 L-10 176 L-0 155 L24 139 L53 127 L77 114 L97 97 L115 78 L138 62 L169 52 L203 50 L237 51 L269 53 L300 52 L331 54 L360 63 L383 81 L398 105 L408 127 L422 141 L446 144 L481 141 L523 140 L563 145 L594 157 L617 174 L638 193 L658 213 L672 236Z'/%3E%3Cpath d='M720 260 L698 286 L660 307 L618 323 L586 338 L570 355 L564 376 L556 398 L538 416 L509 427 L475 432 L443 436 L416 444 L391 457 L364 470 L333 478 L300 481 L267 482 L231 486 L191 496 L143 508 L94 513 L52 503 L27 478 L20 444 L23 409 L27 379 L27 356 L24 335 L23 315 L26 296 L30 277 L25 260 L6 241 L-22 218 L-47 191 L-56 163 L-43 140 L-14 122 L19 109 L47 94 L68 75 L89 53 L116 34 L150 23 L189 20 L228 23 L265 25 L300 25 L335 28 L367 39 L392 60 L409 87 L422 111 L439 124 L468 126 L511 122 L560 120 L605 126 L640 141 L666 161 L688 183 L708 207 L722 233Z'/%3E%3Cpath d='M767 260 L740 288 L697 312 L652 330 L619 347 L603 366 L597 390 L589 415 L568 436 L534 447 L496 452 L460 456 L430 466 L402 481 L372 496 L337 506 L300 510 L262 512 L222 518 L175 529 L122 542 L68 545 L23 532 L-3 502 L-9 463 L-4 424 L1 391 L-0 365 L-6 343 L-9 321 L-7 300 L-6 280 L-13 260 L-36 238 L-68 212 L-94 182 L-102 151 L-85 125 L-51 106 L-14 91 L16 74 L40 52 L62 27 L92 5 L132 -7 L176 -9 L219 -6 L261 -3 L300 -2 L339 3 L374 16 L402 40 L421 69 L435 94 L456 107 L492 107 L542 101 L599 99 L649 107 L688 124 L715 147 L737 173 L758 200 L771 230Z'/%3E%3Cpath d='M812 260 L781 291 L734 317 L685 337 L651 355 L635 378 L631 405 L621 433 L597 455 L559 467 L516 472 L477 477 L444 488 L413 505 L380 523 L341 535 L300 540 L258 543 L212 550 L160 563 L101 576 L41 578 L-6 561 L-32 526 L-37 481 L-30 438 L-24 402 L-27 375 L-35 351 L-41 328 L-42 305 L-43 282 L-53 260 L-79 235 L-114 206 L-143 172 L-148 138 L-127 110 L-89 90 L-47 73 L-14 54 L11 29 L36 1 L69 -23 L113 -36 L162 -38 L211 -34 L257 -30 L300 -27 L342 -22 L381 -7 L411 20 L432 51 L449 77 L474 89 L516 87 L575 80 L639 78 L695 87 L736 107 L764 134 L787 163 L807 194 L818 227Z'/%3E%3Cpath d='M855 260 L821 294 L769 321 L718 343 L683 364 L669 389 L664 420 L654 451 L627 475 L584 487 L537 492 L494 498 L458 510 L425 530 L388 550 L346 564 L300 571 L253 575 L202 583 L144 597 L79 610 L15 610 L-35 589 L-60 548 L-63 498 L-54 451 L-49 413 L-53 384 L-64 359 L-73 334 L-77 309 L-81 285 L-95 260 L-124 233 L-162 200 L-191 162 L-195 126 L-169 95 L-125 74 L-80 55 L-45 34 L-18 6 L9 -26 L46 -52 L94 -66 L149 -67 L203 -61 L253 -56 L300 -52 L346 -45 L387 -28 L420 1 L443 34 L463 60 L493 70 L542 66 L609 57 L680 55 L741 67 L785 90 L814 120 L836 153 L855 188 L865 224Z'/%3E%3Cpath d='M898 260 L859 296 L804 326 L750 350 L715 373 L702 401 L699 435 L687 468 L656 494 L609 507 L557 512 L510 518 L472 532 L436 555 L396 578 L350 594 L300 603 L248 608 L192 617 L128 632 L57 644 L-11 641 L-62 616 L-87 569 L-87 514 L-77 463 L-72 423 L-79 393 L-93 367 L-107 341 L-113 314 L-120 287 L-137 260 L-171 230 L-211 193 L-240 153 L-241 113 L-211 81 L-162 58 L-113 38 L-75 14 L-48 -18 L-18 -53 L22 -81 L76 -95 L136 -95 L194 -88 L249 -81 L300 -76 L349 -68 L394 -49 L428 -18 L453 17 L477 43 L513 51 L570 45 L644 34 L723 32 L789 46 L834 73 L863 107 L884 144 L902 181 L910 221Z'/%3E%3Cpath d='M938 260 L896 299 L838 330 L782 356 L748 382 L736 413 L733 450 L720 486 L686 513 L633 526 L576 531 L527 538 L486 555 L448 580 L405 606 L355 625 L300 635 L243 642 L181 652 L112 667 L36 678 L-36 672 L-89 642 L-112 589 L-110 529 L-98 474 L-94 433 L-104 402 L-123 375 L-141 348 L-151 319 L-161 290 L-182 260 L-218 227 L-261 187 L-289 143 L-287 101 L-252 67 L-198 42 L-146 20 L-106 -7 L-77 -42 L-46 -80 L-1 -110 L57 -124 L123 -123 L187 -114 L245 -106 L300 -100 L352 -90 L400 -68 L437 -36 L464 -0 L492 24 L534 31 L598 22 L682 10 L767 8 L837 24 L884 55 L911 94 L931 134 L947 176 L954 218Z'/%3E%3Cpath d='M978 260 L932 301 L871 335 L815 362 L781 391 L770 425 L768 465 L753 504 L715 532 L657 546 L596 550 L543 558 L500 577 L460 606 L414 636 L360 657 L300 669 L237 676 L170 688 L95 703 L14 712 L-61 703 L-114 666 L-136 608 L-131 543 L-118 485 L-115 442 L-129 411 L-154 383 L-176 355 L-190 324 L-203 293 L-227 260 L-267 223 L-311 180 L-339 133 L-333 88 L-292 52 L-234 26 L-178 3 L-137 -27 L-107 -66 L-74 -107 L-25 -139 L39 -153 L110 -151 L179 -140 L242 -129 L300 -122 L356 -110 L405 -88 L445 -54 L476 -18 L507 5 L556 9 L629 -3 L721 -16 L813 -16 L887 3 L933 38 L960 81 L977 125 L992 170 L996 215Z'/%3E%3Cpath d='M1016 260 L968 303 L904 339 L847 369 L814 400 L805 437 L804 481 L786 522 L743 551 L681 564 L615 569 L559 578 L515 600 L472 632 L423 666 L365 690 L300 703 L232 712 L159 724 L79 738 L-7 746 L-85 732 L-138 690 L-158 626 L-151 556 L-137 495 L-136 451 L-155 420 L-185 392 L-212 362 L-230 329 L-247 295 L-274 260 L-316 220 L-361 174 L-388 123 L-378 76 L-332 38 L-269 11 L-211 -15 L-169 -48 L-138 -90 L-102 -134 L-49 -168 L21 -182 L98 -177 L171 -164 L238 -152 L300 -143 L359 -130 L411 -106 L453 -71 L487 -36 L524 -14 L579 -14 L660 -28 L761 -42 L860 -41 L936 -19 L983 21 L1007 68 L1022 116 L1035 164 L1037 212Z'/%3E%3Cpath d='M1016 120 L1014 122 L1013 124 L1011 126 L1011 128 L1011 131 L1009 133 L1007 135 L1005 136 L1003 138 L1001 141 L999 144 L997 147 L994 150 L989 151 L984 150 L980 148 L976 145 L973 143 L970 142 L967 140 L965 138 L964 136 L962 134 L959 134 L955 133 L950 133 L945 132 L942 130 L941 128 L941 125 L942 122 L941 120 L940 117 L939 115 L940 112 L942 110 L944 108 L947 106 L949 103 L952 101 L955 100 L959 100 L964 101 L969 102 L972 103 L975 102 L977 101 L980 98 L984 96 L988 95 L992 95 L996 95 L1001 95 L1005 95 L1010 96 L1012 99 L1013 102 L1012 106 L1011 109 L1011 111 L1013 113 L1015 115 L1016 118Z'/%3E%3Cpath d='M1052 120 L1049 124 L1045 128 L1043 132 L1042 137 L1041 141 L1039 146 L1035 150 L1030 153 L1026 157 L1023 162 L1020 169 L1015 176 L1008 180 L999 181 L989 179 L980 174 L973 169 L966 166 L960 163 L955 159 L951 156 L948 152 L944 149 L938 148 L929 148 L918 147 L909 145 L904 141 L902 136 L903 130 L903 125 L902 120 L899 115 L898 109 L899 104 L903 99 L909 95 L914 91 L919 87 L924 83 L931 81 L939 80 L949 82 L958 85 L964 86 L969 85 L974 81 L980 76 L987 71 L995 69 L1004 68 L1013 68 L1022 69 L1031 70 L1039 73 L1044 78 L1045 85 L1043 92 L1041 98 L1042 103 L1046 107 L1050 111 L1052 115Z'/%3E%3Cpath d='M1088 120 L1083 127 L1077 133 L1074 139 L1073 145 L1072 152 L1069 159 L1063 165 L1056 170 L1050 176 L1046 184 L1041 195 L1033 204 L1022 211 L1008 211 L993 207 L980 199 L969 192 L960 187 L951 182 L943 178 L937 173 L931 168 L925 164 L915 163 L901 162 L886 161 L872 158 L865 151 L863 143 L865 135 L865 127 L863 120 L859 112 L856 104 L858 96 L865 89 L873 83 L882 77 L889 71 L897 66 L907 62 L921 62 L935 64 L947 68 L957 69 L964 67 L971 60 L980 52 L991 45 L1004 42 L1016 41 L1029 42 L1043 43 L1056 45 L1067 50 L1074 58 L1075 69 L1073 79 L1071 88 L1073 95 L1078 100 L1085 106 L1089 113Z'/%3E%3Cpath d='M1123 120 L1116 129 L1109 137 L1104 145 L1103 153 L1103 163 L1099 172 L1092 180 L1083 188 L1075 196 L1069 208 L1063 221 L1052 234 L1036 241 L1017 241 L997 233 L980 223 L966 214 L954 207 L942 202 L932 196 L923 190 L915 184 L905 180 L891 178 L872 178 L852 176 L835 171 L826 162 L824 151 L827 140 L827 130 L823 120 L818 110 L814 98 L817 88 L826 78 L838 70 L849 63 L860 55 L871 48 L885 44 L903 44 L921 48 L937 52 L949 53 L958 48 L968 38 L980 26 L995 18 L1012 14 L1029 13 L1047 15 L1064 17 L1081 21 L1095 28 L1103 39 L1104 53 L1101 67 L1099 78 L1103 87 L1111 94 L1121 102 L1126 111Z'/%3E%3Cpath d='M1159 120 L1149 131 L1140 141 L1135 151 L1134 162 L1134 174 L1130 186 L1121 196 L1111 206 L1102 217 L1094 232 L1085 249 L1071 263 L1050 271 L1025 269 L1001 259 L980 245 L963 234 L948 226 L934 220 L920 215 L908 208 L897 201 L884 196 L866 195 L842 194 L817 191 L797 184 L787 173 L786 159 L789 145 L789 132 L784 120 L776 107 L772 93 L776 79 L788 68 L803 58 L818 49 L831 40 L846 32 L864 27 L886 27 L908 32 L927 36 L941 36 L952 28 L964 14 L980 -1 L1000 -11 L1021 -16 L1043 -15 L1064 -13 L1085 -9 L1106 -3 L1122 6 L1131 21 L1132 38 L1129 55 L1128 68 L1133 78 L1145 87 L1157 97 L1162 108Z'/%3E%3Cpath d='M1194 120 L1182 133 L1171 145 L1165 157 L1165 170 L1165 185 L1161 199 L1152 212 L1140 225 L1129 239 L1119 257 L1108 277 L1089 293 L1063 301 L1033 296 L1004 283 L980 266 L960 252 L942 244 L925 238 L909 233 L894 226 L880 219 L863 214 L840 212 L811 211 L782 207 L759 198 L748 183 L748 166 L752 150 L751 135 L744 120 L734 104 L729 87 L735 71 L749 57 L769 46 L787 35 L804 25 L822 16 L843 11 L869 11 L895 16 L917 20 L933 18 L946 7 L960 -11 L980 -29 L1004 -41 L1030 -46 L1056 -44 L1081 -40 L1106 -35 L1130 -27 L1148 -15 L1158 3 L1159 24 L1156 43 L1156 58 L1164 70 L1178 80 L1193 92 L1199 106Z'/%3E%3Cpath d='M1229 120 L1214 135 L1201 149 L1196 163 L1197 179 L1198 196 L1194 214 L1183 229 L1170 245 L1157 262 L1145 282 L1130 305 L1108 322 L1077 329 L1041 322 L1008 305 L980 286 L957 270 L937 261 L917 256 L897 251 L879 244 L861 237 L840 232 L813 229 L779 228 L746 223 L720 211 L709 194 L710 174 L715 155 L713 137 L704 120 L692 101 L686 82 L693 63 L711 47 L735 34 L757 22 L777 11 L799 1 L824 -5 L854 -4 L883 1 L907 4 L924 -0 L939 -16 L956 -38 L980 -59 L1009 -73 L1040 -77 L1069 -73 L1098 -67 L1126 -60 L1153 -50 L1174 -35 L1184 -14 L1185 10 L1182 31 L1183 49 L1194 62 L1213 74 L1229 87 L1236 103Z'/%3E%3Cpath d='M1263 120 L1246 137 L1231 153 L1226 169 L1228 188 L1231 208 L1227 228 L1216 247 L1201 265 L1186 285 L1172 309 L1154 333 L1126 351 L1089 357 L1049 347 L1011 326 L980 304 L955 287 L932 278 L909 274 L885 270 L863 263 L842 256 L817 250 L785 248 L747 246 L709 239 L682 224 L671 204 L673 181 L678 159 L675 140 L663 120 L649 99 L643 76 L652 55 L674 37 L702 23 L729 10 L752 -3 L777 -13 L806 -19 L839 -19 L871 -14 L897 -12 L915 -20 L932 -39 L952 -66 L980 -90 L1014 -105 L1049 -108 L1083 -102 L1115 -94 L1146 -84 L1175 -72 L1198 -54 L1209 -30 L1210 -4 L1208 20 L1211 39 L1225 53 L1247 67 L1266 83 L1272 101Z'/%3E%3C/g%3E%3C/svg%3E") left -6rem top -2rem / 1400px auto no-repeat,
    var(--ink-2);
}
.section--path {
  background: var(--grain), var(--ink);
  border-top: 1px solid var(--line);
}
/* the screen block sits in a framed panel behind the sticky intro */
.section--path::before {
  content: ""; position: absolute; z-index: 0; pointer-events: none;
  inset: 0 auto 0 0; width: min(38%, 34rem);
  background:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cg fill='none' stroke='%23a9a2cf' stroke-opacity='.13' stroke-width='1.2'%3E%3Crect x='.6' y='.6' width='62.8' height='62.8'/%3E%3Ccircle cx='32' cy='32' r='17'/%3E%3Cpath d='M0 15a15 15 0 0 0 15-15M49 0a15 15 0 0 0 15 15M64 49a15 15 0 0 0-15 15M15 64A15 15 0 0 0 0 49'/%3E%3C/g%3E%3C/svg%3E")
    0 0 / 64px 64px;
  border-right: 1px solid var(--line);
}
.section--path > * { position: relative; z-index: 1; }
.section--work {
  background:
    var(--grain),
    linear-gradient(rgba(169, 162, 207, .085) 1px, transparent 1px) 0 0 / 100% 120px,
    linear-gradient(90deg, rgba(169, 162, 207, .085) 1px, transparent 1px) 0 0 / 120px 100%,
    linear-gradient(rgba(169, 162, 207, .04) 1px, transparent 1px) 0 0 / 100% 24px,
    linear-gradient(90deg, rgba(169, 162, 207, .04) 1px, transparent 1px) 0 0 / 24px 100%,
    var(--ink-2);
  border-top: 1px solid var(--line);
}
.section--skills {
  background:
    var(--grain),
    repeating-linear-gradient(-45deg, rgba(169, 162, 207, .055) 0 1px, transparent 1px 9px),
    var(--ink);
  border-top: 1px solid var(--line);
}
@media (max-width: 900px) {
  /* one column: the screen block becomes a band across the top instead */
  .section--path::before { width: auto; right: 0; bottom: auto; height: 15rem; border-right: 0; border-bottom: 1px solid var(--line); }
}

.section__head {
  display: flex; align-items: center; gap: 1.25rem;
  margin-bottom: clamp(1.75rem, 4vw, 2.75rem);
}
.kicker {
  flex: none;
  display: flex; align-items: center; gap: .75em;
  width: fit-content;
  font-size: .68rem; letter-spacing: .28em; text-transform: uppercase;
  color: var(--orange);
}
/* the site's banded sunset, shrunk to a section mark. A mask, so it
   takes each kicker's own colour through currentColor. */
.kicker::before {
  content: ""; flex: none;
  width: 1.9em; height: 1.05em;
  background: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 11'%3E%3CclipPath id='c'%3E%3Cpath d='M0 11a10 10 0 0 1 20 0z'/%3E%3C/clipPath%3E%3Cg clip-path='url(%23c)'%3E%3Crect width='20' height='5.6'/%3E%3Crect y='6.8' width='20' height='1.7'/%3E%3Crect y='9.6' width='20' height='1.4'/%3E%3C/g%3E%3C/svg%3E") center / contain no-repeat;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 11'%3E%3CclipPath id='c'%3E%3Cpath d='M0 11a10 10 0 0 1 20 0z'/%3E%3C/clipPath%3E%3Cg clip-path='url(%23c)'%3E%3Crect width='20' height='5.6'/%3E%3Crect y='6.8' width='20' height='1.7'/%3E%3Crect y='9.6' width='20' height='1.4'/%3E%3C/g%3E%3C/svg%3E") center / contain no-repeat;
}
/* a hairline that draws itself across on entry */
/* a hairline with a short heavier lead, like a magazine section rule */
.rule {
  flex: 1; height: 1px;
  background: linear-gradient(90deg, var(--orange) 0 2.5rem, var(--rule) 2.5rem);
}

.h2 {
  font-family: "Syne", sans-serif; font-weight: 700;
  font-size: clamp(1.8rem, 4.4vw, 3.1rem);
  line-height: 1.06; letter-spacing: -.025em;
  margin-bottom: 1.3rem;
}
/* Emphasis changes voice, not colour ramp: the serif italic in one ink. */
.h2 em {
  font-family: "Fraunces", Georgia, serif; font-style: italic; font-weight: 500;
  letter-spacing: -.01em;
  color: var(--orange);
}
.lede {
  color: rgba(241, 230, 208, .72);
  max-width: 54ch;
  margin-bottom: 1rem;
  font-size: 1rem;
}

/* ============================================================
   01 · ORIGIN
   ============================================================ */

/* ============================================================
   THE NAMEPLATE
   ------------------------------------------------------------
   William is the legal name, Preston is the used one. Both show
   up across the site — a certificate says one, the byline says
   the other — so rather than hide either, the parenthetical does
   the explaining: the brackets part and Preston steps out of the
   middle of his own legal name.

   Everything moves with transform and opacity only. The middle
   keeps its full width from the first frame and merely scales,
   so the entrance cannot reflow the page or leave the scroll
   engine's cached section offsets stale.
   ============================================================ */
.nameplate {
  margin-bottom: clamp(2rem, 4.5vw, 3rem);
}

.nameplate__line {
  display: flex; flex-wrap: wrap; align-items: baseline;
  gap: 0 .3em;
  font-family: "Syne", sans-serif; font-weight: 700;
  font-size: clamp(2rem, 6.2vw, 4rem);
  line-height: 1.04; letter-spacing: -.03em;
  color: var(--cream);
}

/* the two outer names arrive from their own sides */
.nameplate__part {
  opacity: 0;
  transition: opacity .7s var(--ease), transform .7s var(--ease),
              color .45s var(--ease);
}
.nameplate__given  { transform: translateX(-22px); }
.nameplate__family { transform: translateX(22px); }
.nameplate.is-revealed .nameplate__given,
.nameplate.is-revealed .nameplate__family { opacity: 1; transform: none; }

/* the bracketed middle */
.nameplate__aka { display: inline-flex; align-items: baseline; }

.nameplate__paren {
  font-style: normal;
  color: var(--orange);
  opacity: 0;
  transition: opacity .55s var(--ease) .22s, transform .7s var(--ease) .22s;
}
/* they start folded together over the centre and swing outward */
.nameplate__paren--l { transform: translateX(.52em) rotate(26deg); transform-origin: 50% 80%; }
.nameplate__paren--r { transform: translateX(-.52em) rotate(-26deg); transform-origin: 50% 80%; }
.nameplate.is-revealed .nameplate__paren { opacity: 1; transform: none; }

.nameplate__pref {
  font-style: normal;
  padding-inline: .06em;
  color: var(--orange);
  opacity: 0;
  transform: scale(.72);
  transform-origin: 50% 70%;
  transition: opacity .55s var(--ease) .42s, transform .7s var(--ease) .42s;
}
.nameplate.is-revealed .nameplate__pref { opacity: 1; transform: none; }

.nameplate__note {
  margin-top: .9rem;
  font-size: .76rem; letter-spacing: .16em; text-transform: uppercase;
  color: var(--muted);
  opacity: 0;
  transition: opacity .6s var(--ease) .7s;
}
.nameplate.is-revealed .nameplate__note { opacity: 1; }

/* Hovering hands the emphasis back to the legal name — the clearest way to
   say these are one person and not two. No gradient is animated here: the
   pref simply steps back and William lights up, both plain colour changes. */
@media (hover: hover) and (pointer: fine) {
  .nameplate__line:hover .nameplate__given { color: var(--gold); }
  .nameplate__line:hover .nameplate__pref { opacity: .3; }
  .nameplate__line:hover .nameplate__paren { opacity: .35; }
  .nameplate__line { cursor: default; }
}

@media (prefers-reduced-motion: reduce) {
  .nameplate__part, .nameplate__paren,
  .nameplate__pref, .nameplate__note {
    opacity: 1; transform: none; transition: none;
  }
}

/* the scroll-lit statement */
.statement {
  font-family: "Syne", sans-serif; font-weight: 600;
  font-size: clamp(1.4rem, 3.2vw, 2.4rem);
  line-height: 1.25; letter-spacing: -.02em;
  max-width: 26ch;
  margin-bottom: clamp(2.5rem, 6vw, 4rem);
  color: var(--cream);
  text-wrap: balance;
}
@media (min-width: 900px) { .statement { max-width: 34ch; } }
/* No will-change. These light up once, on scroll, and then never change
   again — but will-change is a standing promise, not a hint about now, so
   it held 27 separate compositor layers for the life of the page. Chrome
   re-rasterizes every one of them on a zoom, and 27 layers that exist only
   to fade some text is exactly the kind of pressure that makes it start
   dropping tiles. An opacity change on a span is a cheap repaint. */
.statement .word {
  display: inline-block;
  opacity: .16;
}

.origin {
  display: grid; grid-template-columns: 1.1fr .9fr;
  gap: clamp(2rem, 5vw, 4rem);
  align-items: center;
}
/* A grid item's automatic minimum size is its content, which lets a wide
   column overrule the fr track it was given — so as the counters gained
   digits the copy column grew and shoved the portrait sideways. Pinning
   min-width to 0 makes the fr split authoritative and the portrait static. */
.origin > * { min-width: 0; }

/* flex + wrap rather than three rigid columns: with the fr split now
   authoritative the stats have to be able to fold instead of overflow */
.stats {
  display: flex; flex-wrap: wrap;
  column-gap: clamp(1.25rem, 2.6vw, 2.25rem); row-gap: 1.5rem;
  margin-top: 2.4rem;
  padding-top: 2rem;
  border-top: 1px solid var(--line);
}
.stat { display: grid; gap: .3rem; }
.stat__label {
  font-size: .66rem; letter-spacing: .2em; text-transform: uppercase;
  color: var(--muted); order: -1;
}
.stat__num {
  font-family: "Syne", sans-serif; font-weight: 800;
  font-size: clamp(1.5rem, 3.2vw, 2.2rem);
  line-height: 1; white-space: nowrap;
  /* shrink-to-fit, so the reserved digit box is what sets the width */
  justify-self: start;
  /* uniform digit widths, so the number does not jitter as it counts */
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
  color: var(--gold);
}
.stat__num--text { font-size: clamp(1.1rem, 2.2vw, 1.5rem); }

/* ============================================================
   THE PORTRAIT
   ------------------------------------------------------------
   No card, no frame, no rounded box and no 3D rotation anywhere
   in the subtree — so there is nothing that can be clipped, and
   the photograph itself is never distorted. Everything that moves
   sits behind him or is a clipped copy of him.
   ============================================================ */
.portrait { position: relative; }

.portrait__stage {
  position: relative;
  aspect-ratio: 778 / 800;
  isolation: isolate;
}

/* warm light that follows the cursor rather than sitting dead centre */
.portrait__stage::before {
  content: "";
  position: absolute; inset: -14%;
  background: radial-gradient(42% 42% at var(--lx, 50%) var(--ly, 44%),
    rgba(255, 190, 130, .38), rgba(200, 54, 143, .15) 48%, transparent 72%);
  z-index: -4; pointer-events: none;
  animation: glowBreathe 9s ease-in-out infinite;
}

/* ---- the sun, rising behind his shoulders as the section arrives ---- */
.portrait__sun {
  position: absolute; left: 50%; bottom: 22%;
  width: 68%; aspect-ratio: 1;
  /* --dx/--dy are the cursor parallax, --rise the scroll position */
  translate: calc(-50% + var(--dx, 0px)) var(--dy, 0px);
  transform: translateY(calc((1 - var(--rise, 1)) * 48%));
  border-radius: 50%;
  overflow: hidden;
  background: linear-gradient(180deg, #ffeccd 0%, #ffd07a 24%, #ff7a3d 60%, #c8368f 100%);
  box-shadow: 0 0 90px 12px rgba(255, 160, 90, .34);
  z-index: -2;
}
.portrait__sun i {
  position: absolute; left: -10%; width: 120%;
  height: calc(3px + var(--i) * 2.4px);
  top: calc(54% + var(--i) * 9.5%);
  background: rgba(22, 6, 40, .82);
  border-radius: 99px;
}

/* ---- instrument rings, counter-rotating ---- */
.portrait__ring {
  position: absolute; left: 50%; top: 45%;
  translate: calc(-50% + var(--dx, 0px)) calc(-50% + var(--dy, 0px));
  width: 118%; aspect-ratio: 1; overflow: visible;
  z-index: -1; pointer-events: none;
}
.portrait__ring circle {
  fill: none; stroke-width: 1;
  vector-effect: non-scaling-stroke;
  transform-origin: 50% 50%;
}
.portrait__ring circle:first-child {
  stroke: rgba(242, 181, 68, .45);
  stroke-dasharray: 1 7;
  animation: ringTurn 46s linear infinite;
}
.portrait__ring circle:last-child {
  stroke: rgba(232, 86, 44, .55);
  stroke-dasharray: 30 250;
  animation: ringTurn 30s linear infinite reverse;
}

/* ---- the photograph: untouched ---- */
.portrait__photo {
  position: relative; display: block; width: 100%;
  translate: var(--dx, 0px) var(--dy, 0px);
}
.portrait__photo img {
  display: block; width: 100%; height: auto;
  filter: drop-shadow(0 18px 30px rgba(18, 21, 28, .55));
}

/* ---- light passing across him ---- */
.portrait__flash {
  position: absolute; inset: 0;
  display: block; opacity: 0;
  translate: var(--dx, 0px) var(--dy, 0px);
  pointer-events: none;
  /* A gradient mask, not a clip-path: clip-path edges are hard and read as
     a split down his face. This is generated rather than fetched, so unlike
     an image mask it works under file:// too. It multiplies with the photo's
     own alpha, which is what keeps the light on him. */
  -webkit-mask-image: linear-gradient(102deg, transparent 40%, #000 47%, #000 53%, transparent 60%);
          mask-image: linear-gradient(102deg, transparent 40%, #000 47%, #000 53%, transparent 60%);
  -webkit-mask-size: 300% 100%;  mask-size: 300% 100%;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-position: 130% 0;  mask-position: 130% 0;
}
.portrait__flash img {
  display: block; width: 100%; height: auto;
}
/* The brightened copy only exists during the sweep, so the filter is scoped
   to the hover. Left unscoped it held a filter surface the size of the
   photograph permanently, for a layer sitting at opacity 0. */
.portrait:hover .portrait__flash img {
  filter: brightness(1.55) saturate(.6) contrast(.95);
}
.portrait:hover .portrait__flash { animation: flashSweep 1.5s var(--ease); }

/* ============================================================
   02 · PATH — the rail the scroll draws
   ============================================================ */
.path {
  display: grid; grid-template-columns: .8fr 1.2fr;
  gap: clamp(2rem, 5vw, 4rem);
  align-items: start;
}
/* the heading pins while the milestones scroll past it */
.path__intro { position: sticky; top: 22vh; }

.timeline { position: relative; }

.timeline__rail {
  position: absolute; left: 0; top: 0;
  width: 40px; height: 100%;
  overflow: visible; pointer-events: none;
}
.timeline__track {
  stroke: var(--line); stroke-width: 2;
  vector-effect: non-scaling-stroke;
}
.timeline__draw {
  stroke: url(#railGradient); stroke-width: 2; stroke-linecap: round;
  vector-effect: non-scaling-stroke;
  filter: drop-shadow(0 0 6px rgba(232, 86, 44, .5));
}

.timeline__list { display: grid; gap: clamp(2.2rem, 5vw, 3.4rem); }

.milestone {
  position: relative;
  padding-left: clamp(3.5rem, 7vw, 5.5rem);
}
.milestone__dot {
  position: absolute; left: 20px; top: 10px;
  width: 11px; height: 11px; border-radius: 50%;
  translate: -50% 0;
  background: var(--ink-2);
  border: 2px solid rgba(241, 230, 208, .25);
  transition: background .5s var(--ease), border-color .5s var(--ease),
              box-shadow .5s var(--ease), transform .5s var(--ease);
}
.milestone.is-lit .milestone__dot {
  background: var(--gold);
  border-color: var(--orange);
  box-shadow: 0 0 0 5px rgba(232, 86, 44, .16), 0 0 18px rgba(240, 138, 60, .7);
  transform: scale(1.25);
}
.milestone__year {
  display: inline-block;
  font-family: "Syne", sans-serif; font-weight: 700;
  font-size: .72rem; letter-spacing: .18em; text-transform: uppercase;
  color: var(--muted);
  transition: color .5s var(--ease);
}
.milestone.is-lit .milestone__year { color: var(--gold); }
.milestone__title {
  font-family: "Syne", sans-serif; font-weight: 700;
  font-size: clamp(1.15rem, 2.4vw, 1.5rem);
  margin: .3rem 0 .5rem;
}
.milestone__desc { color: rgba(241, 230, 208, .68); max-width: 56ch; font-size: .96rem; }

/* ============================================================
   03 · WORK
   ============================================================ */
.project {
  position: relative; overflow: hidden; isolation: isolate;
  padding: 1.6rem 1.5rem 1.8rem;
  border-radius: var(--r);
  background: var(--ink);
  border: 1px solid var(--line);
  /* No transform-style: preserve-3d. It was doing nothing — `overflow:
     hidden` and `isolation: isolate` both force a 3D context flat, and the
     tilt deliberately parallaxes its children with 2D translate for that
     very reason (see the comment in main.js). All it bought was four more
     promoted layers to re-rasterize on every zoom. */
  transition: transform .4s var(--ease), border-color .4s, box-shadow .4s var(--ease);
}
/* The cursor spotlight was glow-on-glass; the card answers hover with its
   rule and its art instead. The element stays for main.js. */
.project__glow { display: none; }
.project:hover { border-color: var(--orange); }

.project__head {
  display: flex; justify-content: space-between; align-items: center;
  gap: .8rem; margin-bottom: .85rem;
}
.project__index {
  font-family: "Syne", sans-serif; font-weight: 800;
  font-size: .78rem; letter-spacing: .2em; color: var(--orange);
}
.project__title {
  font-family: "Syne", sans-serif; font-weight: 700;
  font-size: 1.45rem; margin-bottom: .3rem;
  transition: color .35s var(--ease), opacity .62s var(--ease);
}
.project:hover .project__title { color: var(--gold); }
.project__meta {
  font-size: .73rem; letter-spacing: .11em; text-transform: uppercase;
  color: var(--gold); margin-bottom: .85rem;
}
.project__meta b { font-weight: 400; opacity: .5; margin-inline: .2rem; }
.project__desc { color: rgba(241, 230, 208, .66); font-size: .92rem; margin-bottom: 1.2rem; }
.project__stack { display: flex; flex-wrap: wrap; gap: .4rem; }
.project__stack li {
  font-size: .68rem; letter-spacing: .07em; padding: .26rem .55rem;
  border-radius: 2px; border: 1px solid rgba(242, 181, 68, .3);
  color: var(--gold);
}

/* ---- the image slot ----------------------------------------------
   Every card reserves a 16:10 box whether or not a file exists yet.
   aspect-ratio means the space is held from first paint, so a lazy
   image arriving later shifts nothing — which matters here because
   the scroll engine caches section offsets and a reflow would leave
   them stale.

   Until an image is dropped in, the box shows a monogram panel. That
   is a designed empty state, not a placeholder: a card with no image
   still looks finished. js/main.js removes an <img> whose file is
   missing so the panel underneath shows through. */
.project__media {
  position: relative;
  aspect-ratio: 16 / 10;
  margin: 0 0 1.35rem;
  border-radius: 2px;
  overflow: hidden;
  background: var(--ink-3);
  border: 1px solid var(--line);
}
/* the same sunset that sits behind the rest of the page */
.project__media::before {
  content: ""; position: absolute; inset: 0;
  /* the fallback panel's horizon: a flat half-sun at the bottom edge */
  background: radial-gradient(circle at 50% 100%, var(--orange) 0 18%, transparent 18%);
  opacity: .5;
}
.project__media::after {
  content: attr(data-monogram);
  position: absolute; inset: 0;
  display: grid; place-items: center;
  font-family: "Syne", sans-serif; font-weight: 800;
  font-size: clamp(2.2rem, 4.4vw, 3.2rem);
  letter-spacing: .05em;
  color: rgba(241, 230, 208, .16);
}
.project__media img {
  position: relative; z-index: 1;
  display: block; width: 100%; height: 100%;
  object-fit: cover;
  /* Slightly oversized so the cursor parallax below can drift the
     image without ever pulling an edge into view. */
  scale: 1.06;
  /* Matches the card's own transition so the image and the card it sits in
     move at the same rate instead of reading as two separate objects. */
  transition: transform .4s var(--ease);
}
/* once a real image is in, the monogram behind it goes away — a PNG with
   transparency would otherwise show the letters through the artwork */
.project__media.has-image::before,
.project__media.has-image::after { display: none; }

/* A cutout or an odd shape is shown whole on the panel rather than cropped
   to a slice of itself. js/main.js sets this by comparing the image's aspect
   ratio to the slot's. The gradient stays visible behind it, which is what
   makes a transparent cutout sit properly on the page. */
.project__media.is-contain img {
  object-fit: contain;
  padding: 5%;
  box-sizing: border-box;
  scale: 1;
}
.project.is-revealed .project__media.is-contain img { scale: 1; }
.project__media.is-contain.has-image::before { display: block; }
/* pixel art is shown whole, on the plain panel — never cropped, no sun behind */
.project__media.is-pixel img { image-rendering: pixelated; object-fit: contain; padding: 6%; box-sizing: border-box; }
.project .project__media.is-pixel img,
.project.is-revealed .project__media.is-pixel img { scale: 1; }
.project__media.is-pixel.has-image::before { display: none; }

/* ---- drawn art ------------------------------------------------------
   Projects without a photo get an illustration of the work itself from
   js/art.js instead of the bare monogram. It sits under the <img>, so a
   real file still wins the moment it is dropped in. */
.project__media .art {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  display: block;
}
.project__media.has-art::before,
.project__media.has-art::after { display: none; }
.project__media.has-image .art { display: none; }

/* small, slow life on hover: the player drifts, the forgotten rooms
   flicker. Only while hovered or focused, so idle cards cost nothing. */
.art .art-float, .art .art-fade { transform-box: fill-box; transform-origin: center; }
.project:hover .art .art-float,
.project:focus-within .art .art-float,
.case__hero .art .art-float { animation: artFloat 3.2s ease-in-out infinite; }
.project:hover .art .art-fade,
.project:focus-within .art .art-fade,
.case__hero .art .art-fade { animation: artFade 2.6s ease-in-out infinite; }
@keyframes artFloat { 50% { transform: translateY(-4px); } }
@keyframes artFade { 50% { opacity: .35; } }
@media (prefers-reduced-motion: reduce) {
  .art .art-float, .art .art-fade { animation: none !important; }
}

.project__badge {
  flex: none;
  font-size: .6rem; letter-spacing: .16em; text-transform: uppercase;
  padding: .3rem .55rem; border-radius: 2px;
  border: 1px solid rgba(241, 230, 208, .18);
  color: rgba(241, 230, 208, .58);
}
.project__badge--win {
  color: var(--gold);
  border-color: rgba(242, 181, 68, .42);
  background: rgba(242, 181, 68, .08);
}

.project.is-revealed::after { transform: translateY(-101%); }

/* 3. the artwork settling out of its overscale */
.project__media img,
.project__media::after {
  transition: scale 1.25s var(--ease) var(--rd, 0s),
              opacity .8s var(--ease) calc(var(--rd, 0s) + .25s);
}
/* Scoped to .project on purpose. The case-study pages reuse .project__media
   for their own image slots, and those have no card wrapping them — an
   unscoped pre-state would leave them permanently overscaled with an
   invisible monogram. */
.project .project__media img { scale: 1.16; }
.project.is-revealed .project__media img { scale: 1.06; }
.project .project__media::after { scale: 1.3; opacity: 0; }   /* the monogram */
.project .project__media .art {
  scale: 1.12; opacity: 0;
  transition: scale 1.25s var(--ease) var(--rd, 0s), opacity .8s var(--ease) calc(var(--rd, 0s) + .15s);
}
.project.is-revealed .project__media .art { scale: 1; opacity: 1; }
.project.is-revealed .project__media::after { scale: 1; opacity: 1; }

/* 4 + 5. the rows, in order */
/* Scoped to .project throughout. These class names are reused on the
   case-study pages, where there is no card to reveal them — an unscoped
   pre-state left the badge and the tool tags permanently at opacity 0. */
.project .project__index,
.project .project__badge,
.project .project__title,
.project .project__meta,
.project .project__desc,
.project .project__stack {
  opacity: 0;
  transform: translateY(12px);
  transition: opacity .62s var(--ease), transform .62s var(--ease);
}
.project .project__index { transform: translateX(-14px); }
/* The title is the one element here that must never carry a transform. The
   card-wide hit area is a pseudo-element of the link inside it, and a
   transform on an ancestor makes that ancestor the containing block — which
   silently shrinks the clickable area from the whole card down to the title's
   own line box. It fades in without moving, and answers hover with colour. */
.project .project__title { transform: none; }
.project.is-revealed .project__index,
.project.is-revealed .project__badge,
.project.is-revealed .project__title,
.project.is-revealed .project__meta,
.project.is-revealed .project__desc,
.project.is-revealed .project__stack {
  opacity: 1; transform: none;
}
.project.is-revealed .project__index { transition-delay: calc(var(--rd, 0s) + .34s); }
.project.is-revealed .project__badge { transition-delay: calc(var(--rd, 0s) + .40s); }
.project.is-revealed .project__title { transition-delay: calc(var(--rd, 0s) + .44s); }
.project.is-revealed .project__meta  { transition-delay: calc(var(--rd, 0s) + .52s); }
.project.is-revealed .project__desc  { transition-delay: calc(var(--rd, 0s) + .58s); }
.project.is-revealed .project__stack { transition-delay: calc(var(--rd, 0s) + .64s); }

/* ---- the whole card is a link ----
   The anchor wraps the title so the link has real, readable text, and its
   ::after stretches over the card to make the whole thing clickable. */
.project__title a {
  color: inherit; text-decoration: none;
}
.project__title a::after {
  content: ""; position: absolute; inset: 0; z-index: 5;
  border-radius: inherit;
}
/* The arrow is a real link now, sitting above the card-wide hit area so a
   click on it lands on the arrow itself. It is kept out of the accessibility
   tree — the title is already a link to the same page, and a second tab stop
   for the same destination is noise for anyone using a keyboard or a screen
   reader. This is a pointer affordance, and it behaves like one. */
.project__arrow {
  position: relative; z-index: 6;
  display: grid; place-items: center;
  flex: none;
  width: 38px; height: 38px;
  margin: -7px -6px -7px 0;
  border-radius: var(--r);
  border: 1px solid var(--line);
  color: var(--gold); text-decoration: none;
  font-size: .95rem; line-height: 1;
  transition: background .35s var(--ease), border-color .35s,
              color .35s, transform .2s var(--ease);
}
.project__arrow span { display: block; transition: transform .35s var(--ease); }
.project:hover .project__arrow { border-color: rgba(242, 181, 68, .42); }
.project__arrow:hover {
  background: var(--orange);
  border-color: transparent; color: var(--ink);
}
.project__arrow:hover span { transform: translate(2px, -2px); }
.project__arrow:active { transform: scale(.9); }
.project:focus-within { border-color: var(--orange); }
.project__title a:focus-visible { outline: none; }
.project:focus-within::before {
  content: ""; position: absolute; inset: 4px; z-index: 9;
  border: 2px solid var(--pool); border-radius: 2px; pointer-events: none;
}

/* Reduced motion: no sweep, no overscale, no stagger — just the content. */
@media (prefers-reduced-motion: reduce) {
  .project::after { display: none; }
  .project__media img { scale: 1.06; }
  .project__media::after { scale: 1; opacity: 1; }
  .project .project__media .art { scale: 1; opacity: 1; }
  .discipline__icon { stroke-dashoffset: 0; }
  .project__index, .project__badge, .project__title,
  .project__meta, .project__desc, .project__stack {
    opacity: 1; transform: none; transition: none;
  }
}

.project__arrow {
  font-size: 1.1rem; line-height: 1; color: var(--cream); opacity: .3;
  transition: transform .4s var(--ease), opacity .4s;
}

/* ============================================================
   04 · CAPABILITIES
   ============================================================ */
.skills {
  display: grid; grid-template-columns: .85fr 1.15fr;
  gap: clamp(2rem, 5vw, 4rem); align-items: start;
}
/* The tile grid is much taller than the intro beside it, so the intro rides
   along rather than leaving a column of empty space — the same treatment the
   Path section gives its heading. */
@media (min-width: 900px) {
  .skills__intro { position: sticky; top: 22vh; }
}
@media (max-width: 900px) {
  .skills { grid-template-columns: 1fr; }
}
/* A hairline grid: the 1px gap over a --line background is what draws the
   rules between tiles, so there is one border rather than eight. */
.disciplines {
  display: grid; grid-template-columns: repeat(2, 1fr);
  gap: 1px; background: var(--line);
  border: 1px solid var(--line); border-radius: var(--r); overflow: hidden;
}
.discipline {
  position: relative;
  background: var(--ink-2);
  padding: 1.5rem 1.4rem 1.7rem;
  transition: background .45s var(--ease);
}
.discipline:hover { background: #1f2430; }

/* each discipline's own line icon, drawn on as the tile reveals.
   pathLength="1" on every shape makes one dash length fit them all,
   and stroke-dash* inherit through <use> into the sprite. */
.discipline__icon {
  position: absolute; top: 1.35rem; right: 1.3rem;
  width: 1.7rem; height: 1.7rem;
  fill: none; stroke: var(--gold); stroke-width: 1.5;
  stroke-linecap: round; stroke-linejoin: round;
  stroke-dasharray: 1; stroke-dashoffset: 1;
  opacity: .8;
  transition: stroke-dashoffset 1.4s var(--ease) calc(var(--rd, 0s) + .25s),
              transform .5s var(--ease), stroke .4s, opacity .4s;
}
.discipline.is-revealed .discipline__icon { stroke-dashoffset: 0; }
.discipline:hover .discipline__icon { stroke: var(--orange); opacity: 1; transform: translateY(-2px) rotate(-6deg); }
.discipline__name { padding-right: 2.4rem; }
.discipline__name {
  display: flex; align-items: baseline; gap: .6rem;
  font-family: "Syne", sans-serif; font-weight: 700; font-size: 1.02rem;
  margin-bottom: .9rem;
}
.discipline__idx { font-size: .66rem; letter-spacing: .18em; color: var(--orange); }
.discipline__list { display: grid; gap: .42rem; }
.discipline__list li {
  font-size: .88rem; color: rgba(241, 230, 208, .7);
  padding-left: .95rem; position: relative;
}
.discipline__list li::before {
  content: ""; position: absolute; left: 0; top: .62em;
  width: 5px; height: 1px; background: var(--orange); opacity: .8;
}

/* ============================================================
   05 · CONTACT
   ============================================================ */
.section--contact {
  position: relative;
  /* the hero's printed sky again, so the page ends where it began */
  background: linear-gradient(180deg, var(--ink) 0 34%, var(--deep) 34% 50%, var(--violet) 50% 61%,
    var(--magenta) 61% 73%, var(--rose) 73% 85%, var(--blush) 85% 100%);
  border-top: 1px solid var(--line);
  padding-bottom: 0;
  min-height: 88vh;
  display: flex; flex-direction: column; justify-content: center;
}
.contact__scene { position: absolute; inset: 0; overflow: hidden; z-index: 0; }
.contact__sun {
  /* Low enough that its top clears the links above it — cream text on the
     sun's cream crown was unreadable. A flat printed disc, not a bloom:
     the page's last sun matches its first. */
  position: absolute; left: 50%; bottom: 3%; translate: -50% 0;
  width: min(40vw, 360px); aspect-ratio: 1; border-radius: 50%;
  background: linear-gradient(180deg, var(--sun-top) 0 24%, var(--sun-mid) 24% 50%, var(--sun-low) 50% 74%, var(--orange) 74% 100%);
  /* cut by the same bands as the hero sun */
  -webkit-mask: linear-gradient(180deg, #000 0 58%, transparent 58% 61%, #000 61% 70%, transparent 70% 74%,
    #000 74% 82%, transparent 82% 87%, #000 87%);
          mask: linear-gradient(180deg, #000 0 58%, transparent 58% 61%, #000 61% 70%, transparent 70% 74%,
    #000 74% 82%, transparent 82% 87%, #000 87%);
}
.palms--footer { height: 42%; bottom: 0; z-index: 1; --windk-layer: .6; }

.contact__inner { position: relative; z-index: 2; text-align: center; padding-bottom: 7rem; }
.contact__inner .kicker { color: var(--gold); margin-inline: auto; }
.contact__title { margin-bottom: 1.9rem; }

.mailto {
  display: inline-flex; align-items: center; gap: .75rem;
  font-family: "Syne", sans-serif; font-weight: 700;
  font-size: clamp(1.05rem, 2.8vw, 1.8rem);
  padding: .85rem 1.6rem; border-radius: var(--r);
  color: var(--ink); background: var(--cream);
  box-shadow: 5px 5px 0 var(--ink);
  transition: background .35s;
}
.mailto:hover { background: var(--gold); }
.mailto svg { width: 19px; height: 19px; fill: none; stroke: currentColor; stroke-width: 2; transition: transform .35s var(--ease); }
.mailto:hover svg { transform: translateX(5px); }

.socials { display: flex; justify-content: center; gap: 1.9rem; margin-top: 2rem; }
.socials a {
  font-size: .82rem; letter-spacing: .16em; text-transform: uppercase;
  color: rgba(241, 230, 208, .82); position: relative; padding-bottom: 3px;
  text-shadow: 0 2px 12px rgba(18, 21, 28, .5);
}
.socials a::after {
  content: ""; position: absolute; left: 0; bottom: 0; width: 100%; height: 1px;
  background: var(--cream); transform: scaleX(0); transform-origin: right;
  transition: transform .4s var(--ease);
}
.socials a:hover { color: #fff; }
.socials a:hover::after { transform: scaleX(1); transform-origin: left; }

.footer {
  position: relative; z-index: 3; margin-top: auto;
  display: flex; flex-wrap: wrap; gap: 1rem; justify-content: space-between;
  padding: 1.1rem var(--gutter);
  /* solid: a translucent bar over the peach horizon turned muddy brown */
  background: var(--ink);
  border-top: 1px solid var(--line);
  font-size: .74rem; letter-spacing: .09em; color: rgba(241, 230, 208, .58);
}
.footer__made { display: inline-flex; align-items: center; gap: .5rem; }
.footer__pulse {
  width: 6px; height: 6px; border-radius: 50%; background: var(--orange);
  animation: statusPulse 2.6s ease-out infinite;
}

/* ============================================================
   RESPONSIVE
   ============================================================ */
@media (max-width: 980px) {
  .origin, .skills, .path { grid-template-columns: 1fr; }
  .path__intro { position: static; }
}

@media (max-width: 700px) {
  .disciplines { grid-template-columns: 1fr; }
  .section__head { gap: .9rem; }
  .stats { column-gap: 2rem; row-gap: 1.4rem; }
  .socials { gap: 1.1rem; flex-wrap: wrap; }
  .contact__sun { width: min(68vw, 300px); bottom: 5%; }
  .mailto { font-size: clamp(.8rem, 3.6vw, 1.05rem); padding: .8rem .9rem; gap: .5rem; max-width: 100%; }
  .mailto span { overflow: hidden; text-overflow: ellipsis; }
  .footer { justify-content: center; text-align: center; }
  .milestone { padding-left: 3.2rem; }
  .timeline__rail { width: 30px; }
  .milestone__dot { left: 15px; }
}

/* ============================================================
   THE SHELF
   ------------------------------------------------------------
   Projects sit on one horizontal shelf instead of a vertical
   grid. The point is that the section's height is fixed: eight
   projects and eighty projects occupy the same space on the
   page, so adding work never turns the site into a long scroll.

   Discovery is handled by the three things around the shelf —
   filter chips built from each card's data-kind, a position
   counter, and explicit prev/next buttons — rather than by
   making the visitor scroll further.

   No scroll hijacking: the shelf is a real scroll container, so
   a trackpad, a touchscreen, a keyboard and the buttons all
   work, and the page's own vertical scrolling is untouched.
   ============================================================ */
.shelf { margin-top: clamp(1.5rem, 3.5vw, 2.2rem); }

.shelf__bar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem 1.4rem; flex-wrap: wrap;
}

.shelf__filters { display: flex; flex-wrap: wrap; gap: .5rem; }
.shelf__chip {
  font: inherit; font-size: .73rem; letter-spacing: .05em;
  padding: .44rem .8rem; border-radius: 2px;
  cursor: pointer;
  border: 1px solid var(--line);
  background: transparent;
  color: rgba(241, 230, 208, .62);
  transition: color .3s, border-color .3s, background .3s, transform .25s var(--ease);
}
.shelf__chip:hover { color: var(--cream); border-color: rgba(242, 181, 68, .4); }
.shelf__chip:active { transform: scale(.96); }
.shelf__chip[aria-pressed="true"] {
  color: var(--ink); font-weight: 600;
  background: var(--cream);
  border-color: transparent;
}
.shelf__chip b { font-weight: inherit; opacity: .5; margin-left: .4rem; }
.shelf__chip[aria-pressed="true"] b { opacity: .6; }

.shelf__nav { display: flex; align-items: center; gap: .55rem; }
.shelf__count {
  margin: 0 .3rem 0 0;
  font-size: .68rem; letter-spacing: .18em; color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.shelf__btn {
  width: 40px; height: 40px; flex: none;
  display: grid; place-items: center;
  border-radius: 50%;
  border: 1px solid var(--line);
  background: rgba(241, 230, 208, .03);
  color: var(--cream); cursor: pointer;
  font: inherit; font-size: 1rem;
  transition: border-color .3s, background .3s, opacity .3s, transform .25s var(--ease);
}
.shelf__btn:hover:not(:disabled) {
  border-color: var(--gold); background: rgba(242, 181, 68, .12);
  transform: translateY(-2px);
}
.shelf__btn:active:not(:disabled) { transform: scale(.94); }
.shelf__btn:disabled { opacity: .28; cursor: default; }

/* The track bleeds past the wrap on purpose, so the next card is always
   half-visible at the edge — the clearest possible signal that there is
   more to the right without adding any chrome to say so. */
.shelf__track {
  display: flex; align-items: stretch;
  gap: 1.2rem;
  overflow-x: auto; overflow-y: hidden;
  scroll-snap-type: x mandatory;
  overscroll-behavior-x: contain;
  /* the padding is what keeps the hover lift and its shadow from being
     clipped by the scroll container */
  padding: 1.5rem var(--gutter) 2.2rem;
  scroll-padding-inline: var(--gutter);
  margin-top: 1.3rem;
  scrollbar-width: none;
}
.shelf__track::-webkit-scrollbar { display: none; }
.shelf__track:focus-visible { outline: 2px solid var(--gold); outline-offset: -6px; }

.shelf__track .project {
  flex: 0 0 auto;
  width: clamp(15.5rem, 26vw, 20.5rem);
  scroll-snap-align: start;
  display: flex; flex-direction: column;
}
/* the lead project gets more room and a taller image, so the shelf still
   has a hierarchy rather than reading as eight identical tiles */
.shelf__track .project--lead { width: clamp(18rem, 33vw, 26rem); }
.shelf__track .project--lead .project__media { aspect-ratio: 4 / 3; }
.shelf__track .project--lead .project__title { font-size: 1.7rem; }

.shelf__track .project__body { display: flex; flex-direction: column; flex: 1; }
/* A teaser, not the whole story — the case study has the full text. Clamping
   keeps every card close to the same height, which is what stops a shelf of
   mixed-length copy from looking ragged. */
.shelf__track .project__desc {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  margin-bottom: 0;
}
/* tags settle on the baseline of every card, whatever the copy length */
.shelf__track .project__stack { margin-top: auto; padding-top: 1rem; }
.shelf__track .project[hidden] { display: none; }

.shelf__progress {
  height: 2px; margin-top: .2rem;
  background: var(--line);
  overflow: hidden;
}
.shelf__progress span {
  display: block; height: 100%; width: 0;
  background: var(--orange);
  transition: width .18s linear;
}
.shelf__empty {
  padding: 2.5rem var(--gutter);
  color: var(--muted); font-size: .9rem;
}

@media (max-width: 700px) {
  .shelf__bar { gap: .9rem; }
  .shelf__nav { width: 100%; justify-content: flex-end; }
  .shelf__track .project,
  .shelf__track .project--lead { width: min(80vw, 20rem); }
  .shelf__track .project--lead .project__title { font-size: 1.45rem; }
}

@media (prefers-reduced-motion: reduce) {
  .shelf__progress span { transition: none; }
  .shelf__track { scroll-behavior: auto; }
}

/* ---- what sits inside a discipline tile ---- */
.discipline__tools {
  font-size: .72rem; letter-spacing: .08em;
  color: var(--gold);
  margin-bottom: .7rem;
}
.discipline__note {
  font-size: .88rem; line-height: 1.6;
  color: rgba(241, 230, 208, .62);
  margin: 0;
  text-wrap: pretty;
}

/* ============================================================
   CERTIFICATIONS
   ------------------------------------------------------------
   The certificates are white documents, so each sits on a white
   panel rather than being punched into the dark page — it reads
   as a document that way instead of a glowing rectangle. Each
   links to the full-size file.
   ============================================================ */
.certs__head {
  font-family: "Syne", sans-serif; font-weight: 700;
  font-size: clamp(1.1rem, 2.4vw, 1.5rem);
  margin: clamp(2.5rem, 5vw, 3.5rem) 0 1.2rem;
}
.certs {
  display: grid; gap: 1.2rem;
  grid-template-columns: repeat(auto-fit, minmax(19rem, 1fr));
}
.cert {
  display: grid; gap: 1.1rem;
  padding: 1.3rem;
  border: 1px solid var(--line); border-radius: var(--r);
  background: var(--ink-2);
  transition: border-color .4s;
}
.cert:hover { border-color: var(--orange); }

.cert__shot {
  display: block; overflow: hidden;
  border-radius: 2px;
  border: 1px solid rgba(241, 230, 208, .14);
  background: #fff;
  line-height: 0;
}
.cert__shot img {
  display: block; width: 100%; height: auto;
  transition: scale .6s var(--ease);
}
.cert:hover .cert__shot img { scale: 1.02; }

.cert__meta {
  font-size: .68rem; letter-spacing: .16em; text-transform: uppercase;
  color: var(--gold); margin-bottom: .35rem;
}
.cert__meta b, .cert__id b { font-weight: 400; opacity: .5; margin-inline: .25rem; }
.cert__name {
  font-family: "Syne", sans-serif; font-weight: 700;
  font-size: 1.02rem; line-height: 1.25;
  margin-bottom: .5rem;
}
.cert__desc {
  font-size: .86rem; line-height: 1.6;
  color: rgba(241, 230, 208, .62);
  margin-bottom: .7rem;
}
.cert__id {
  font-size: .7rem; letter-spacing: .06em;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  margin: 0;
}

@media (max-width: 700px) {
  .disciplines { grid-template-columns: 1fr; }
}
