experience.css (8927B)
1 #experience { 2 width: 100%; 3 } 4 5 .experience-layout { 6 display: flex; 7 flex-direction: row; 8 height: 100%; 9 width: 100%; 10 padding: 24px 32px; 11 gap: 24px; 12 justify-content: center; 13 } 14 15 .experience-detail { 16 flex: 0 1 980px; 17 width: 100%; 18 overflow-y: auto; 19 display: flex; 20 flex-direction: column; 21 gap: 16px; 22 padding: 4px 20px 4px 4px; 23 } 24 25 .exp-row { 26 display: flex; 27 } 28 29 .exp-card { 30 background: var(--bg-alt); 31 border: 2px solid var(--border); 32 border-radius: 4px; 33 padding: 24px; 34 flex: 1; 35 min-width: 0; 36 display: flex; 37 flex-direction: column; 38 transition: border-color 80ms linear; 39 } 40 41 .exp-row.active .exp-card { 42 border-color: var(--accent); 43 } 44 45 /* 46 * Timeline strip: one lane per job on a shared month axis. 47 * 48 * hark | | | ======| 9 mo 49 * mandolin | ==|=======|== | 1 yr 5 mo 50 * amplifier | == | | | 3 mo 51 * 2024 2025 2026 now 52 * 53 * A bar fills its start month through its end month. Each lane is a link 54 * to its job and lights up together with the job's card. 55 */ 56 .exp-chart { 57 --name-w: 18ch; 58 --dur-w: 10ch; 59 position: sticky; 60 top: -4px; 61 z-index: 1; 62 display: grid; 63 grid-template-columns: var(--name-w) minmax(0, 1fr) var(--dur-w); 64 column-gap: 2ch; 65 /* sit beside the graph rail, never over it: start at the card's edge and 66 line the lanes up with the card text */ 67 padding: 12px 24px 8px; 68 margin: -4px 0 0 3ch; 69 background: var(--bg); 70 border-bottom: 1px solid var(--border); 71 font-size: var(--fs-14); 72 line-height: 1.5; 73 user-select: none; 74 } 75 76 .chart-grid { 77 grid-column: 2; 78 position: relative; 79 pointer-events: none; 80 } 81 82 .grid-line { 83 position: absolute; 84 top: 0; 85 bottom: 0; 86 left: var(--at); 87 width: 1px; 88 background: var(--border); 89 } 90 91 .exp-lane { 92 grid-column: 1 / -1; 93 display: grid; 94 grid-template-columns: subgrid; 95 align-items: center; 96 color: var(--fg-dim); 97 text-decoration: none; 98 border-radius: 2px; 99 outline-offset: 2px; 100 -webkit-tap-highlight-color: transparent; 101 } 102 103 .exp-lane:hover { 104 text-decoration: none; 105 } 106 107 .exp-lane:focus-visible { 108 outline: 1px solid var(--accent); 109 } 110 111 .lane-name { 112 overflow: hidden; 113 text-overflow: ellipsis; 114 white-space: nowrap; 115 } 116 117 .lane-dur { 118 text-align: right; 119 white-space: nowrap; 120 } 121 122 .lane-track { 123 position: relative; 124 height: 1.5em; 125 } 126 127 .exp-bar { 128 position: absolute; 129 top: 50%; 130 left: var(--from); 131 width: calc(var(--to) - var(--from)); 132 min-width: 4px; 133 height: 6px; 134 transform: translateY(-50%); 135 background: var(--fg-dim); 136 transition: background-color 80ms linear; 137 } 138 139 /* start and end caps: |=====| */ 140 .exp-bar::before, 141 .exp-bar::after { 142 content: ""; 143 position: absolute; 144 top: -4px; 145 bottom: -4px; 146 width: 2px; 147 background: inherit; 148 } 149 150 .exp-bar::before { 151 left: 0; 152 } 153 154 .exp-bar::after { 155 right: 0; 156 } 157 158 /* still running: no end cap, the bar runs into the "now" edge */ 159 .exp-bar.current::after { 160 display: none; 161 } 162 163 .exp-lane.active { 164 color: var(--accent); 165 } 166 167 .exp-lane.active .exp-bar { 168 background: var(--accent); 169 } 170 171 .chart-axis { 172 grid-column: 2; 173 color: var(--fg-dim); 174 font-size: var(--fs-12); 175 } 176 177 .axis-labels { 178 position: relative; 179 display: block; 180 height: 1.5em; 181 } 182 183 .axis-tick { 184 position: absolute; 185 left: var(--at); 186 padding-left: 4px; 187 white-space: nowrap; 188 } 189 190 .axis-end { 191 position: absolute; 192 right: 0; 193 white-space: nowrap; 194 } 195 196 /* 197 * Graph rail, jj log style: one continuous line down the list with a node 198 * on each job's role line, newest first, ending at the oldest. 199 * 200 * @ hark / ... 201 * │ 202 * * mandolin / ... 203 * │ 204 * * amplifier health / ... 205 * 206 * Dates live on the cards, so the rail carries only the shape. The active 207 * job lights its node and the line down to the job before it. 208 */ 209 .exp-timeline { 210 --row: 1.5em; 211 --node-top: 46px; /* card border + padding + role line, beside the logo */ 212 flex-shrink: 0; 213 width: 3ch; 214 position: relative; 215 font-size: var(--fs-14); 216 line-height: 1.5; 217 font-variant-ligatures: none; 218 font-feature-settings: "liga" 0, "calt" 0; 219 font-synthesis: none; 220 color: var(--fg-dim); 221 cursor: default; 222 user-select: none; 223 } 224 225 .exp-row:has(.logo-hark) .exp-timeline { 226 --node-top: 39px; 227 } 228 229 .graph-node { 230 position: absolute; 231 left: 0; 232 width: 1ch; 233 height: var(--row); 234 text-align: center; 235 } 236 237 .graph-node { 238 top: var(--node-top); 239 transition: color 80ms linear; 240 } 241 242 /* hairline through the node column, broken around each node */ 243 .exp-row + .exp-row .exp-timeline::before, 244 .exp-row:not(:last-child) .exp-timeline::after { 245 content: ""; 246 position: absolute; 247 left: calc(0.5ch - 0.5px); 248 width: 1px; 249 background: var(--border); 250 transition: background-color 80ms linear; 251 } 252 253 /* from the previous job's line, across the gap, down to this node */ 254 .exp-row + .exp-row .exp-timeline::before { 255 top: -16px; 256 height: calc(16px + var(--node-top)); 257 } 258 259 /* from under this node to the bottom of the row */ 260 .exp-row:not(:last-child) .exp-timeline::after { 261 top: calc(var(--node-top) + var(--row)); 262 bottom: 0; 263 } 264 265 .exp-row.active .graph-node { 266 color: var(--accent); 267 } 268 269 .exp-row.active .exp-timeline::after, 270 .exp-row.active + .exp-row .exp-timeline::before { 271 background: var(--accent-dim); 272 } 273 274 @media (prefers-reduced-motion: reduce) { 275 .exp-card, 276 .exp-bar, 277 .graph-node, 278 .exp-timeline::before, 279 .exp-timeline::after { 280 transition: none; 281 } 282 } 283 284 .exp-card-header { 285 display: grid; 286 grid-template-columns: minmax(0, 1fr) auto; 287 align-items: flex-start; 288 gap: 12px 24px; 289 } 290 291 .exp-card-title { 292 min-width: 0; 293 } 294 295 .exp-title-with-logo { 296 display: flex; 297 align-items: flex-start; 298 gap: 12px; 299 min-width: 0; 300 } 301 302 .exp-logo { 303 height: 44px; 304 width: auto; 305 max-width: 180px; 306 object-fit: contain; 307 flex-shrink: 0; 308 } 309 310 .exp-logo.logo-hark { 311 height: 30px; 312 max-width: 140px; 313 } 314 315 .exp-logo.logo-invert { 316 filter: brightness(0) invert(1); 317 opacity: 0.9; 318 } 319 320 .exp-title-text { 321 display: flex; 322 align-items: center; 323 gap: 10px; 324 flex-wrap: wrap; 325 min-width: 0; 326 } 327 328 .exp-title-text h2 { 329 font-size: var(--fs-22); 330 font-weight: 500; 331 color: var(--accent); 332 margin: 0; 333 line-height: 1.1; 334 } 335 336 .exp-role { 337 display: inline-flex; 338 align-items: center; 339 color: var(--accent); 340 font-size: var(--fs-15); 341 font-weight: 500; 342 white-space: nowrap; 343 line-height: 1.2; 344 } 345 346 .exp-role::before { 347 content: "/"; 348 color: var(--yellow); 349 margin-right: 4px; 350 } 351 352 .exp-role::after { 353 content: ""; 354 } 355 356 .exp-title-with-logo .exp-role { 357 font-size: var(--fs-16); 358 line-height: 1.1; 359 margin-top: 22px; 360 } 361 362 .exp-logo.logo-hark + .exp-role { 363 margin-top: 15px; 364 } 365 366 .exp-card-meta { 367 display: flex; 368 flex-direction: column; 369 align-items: flex-end; 370 gap: 3px; 371 font-size: var(--fs-14); 372 text-align: right; 373 white-space: nowrap; 374 } 375 376 .exp-duration { 377 color: var(--fg-dim); 378 } 379 380 .exp-card-tech { 381 display: flex; 382 flex-wrap: wrap; 383 gap: 6px; 384 margin-bottom: 20px; 385 } 386 387 .exp-card-body { 388 color: var(--fg); 389 line-height: 1.7; 390 } 391 392 .exp-card-body p { 393 margin-bottom: 12px; 394 } 395 396 .exp-card-images { 397 display: flex; 398 gap: 12px; 399 margin-top: 12px; 400 padding-top: 12px; 401 border-top: 1px solid var(--border); 402 max-height: 200px; 403 overflow: hidden; 404 } 405 406 .exp-image-hero { 407 max-width: 280px; 408 height: 100%; 409 border-radius: 4px; 410 border: 1px solid var(--border); 411 object-fit: cover; 412 cursor: pointer; 413 } 414 415 .exp-image-grid { 416 display: flex; 417 flex-wrap: wrap; 418 gap: 8px; 419 max-width: 400px; 420 } 421 422 .exp-image-thumb { 423 width: calc(50% - 4px); 424 height: 86px; 425 border-radius: 4px; 426 border: 1px solid var(--border); 427 object-fit: cover; 428 cursor: pointer; 429 } 430 431 .external-link { 432 color: var(--light-blue); 433 font-size: var(--fs-14); 434 max-width: 36ch; 435 overflow: hidden; 436 text-overflow: ellipsis; 437 } 438 439 @media (max-width: 768px) { 440 .experience-layout { 441 padding: 16px; 442 gap: 12px; 443 } 444 445 .exp-timeline { 446 display: none; 447 } 448 449 .exp-chart { 450 --name-w: 9ch; 451 --dur-w: 9ch; 452 position: static; 453 column-gap: 1ch; 454 margin: 0; 455 padding: 0 0 8px; 456 border-bottom: 0; 457 } 458 459 .lane-track, 460 .exp-lane { 461 min-height: 2em; 462 } 463 464 .exp-card { 465 padding: 16px; 466 } 467 468 .experience-detail { 469 padding: 4px 14px 4px 0; 470 } 471 472 .exp-card-header { 473 grid-template-columns: 1fr; 474 gap: 10px; 475 } 476 477 .exp-title-with-logo { 478 align-items: flex-start; 479 } 480 481 .exp-logo { 482 height: 38px; 483 max-width: 150px; 484 margin-top: 0; 485 } 486 487 .exp-logo.logo-hark { 488 height: 26px; 489 max-width: 120px; 490 } 491 492 .exp-title-with-logo .exp-role { 493 margin-top: 19px; 494 } 495 496 .exp-logo.logo-hark + .exp-role { 497 margin-top: 13px; 498 } 499 500 .exp-card-meta { 501 align-items: flex-start; 502 text-align: left; 503 white-space: normal; 504 } 505 506 .exp-card-images { 507 flex-direction: column; 508 max-height: none; 509 overflow: visible; 510 } 511 512 .exp-image-hero { 513 max-width: 100%; 514 height: auto; 515 max-height: 200px; 516 } 517 518 .exp-image-thumb { 519 width: calc(33.33% - 6px); 520 height: 80px; 521 } 522 }