feat: 프로젝트 공통 지반모델·교대 지반 통합·재료표/수량 진입·옥성교 랜딩 히어로 (11-06~11-09 작업분)

공용 파일(main.ts·index.html·style.css)에서 서로 얽혀 있어 한 커밋으로 묶음.
- 지반: ① 형상 프로젝트 지반모델 관리창(생성·JSON 불러오기·교각별 비교·선택 적용),
  프로젝트 JSON 내장 groundModel 추출, 교대 8행 통합 적용·공통지형 중복격자 제외
- 재료표·수량: 상단 ⑤ 메뉴·배근 바로가기, 결과 교각/탭 기억, 수량 워크북·정합 보정
- 랜딩: 옥성교 6교각 히어로 이미지·투영 강조점·좌→우 광선·지형 라운드/도로 연결
- 검증 스크립트(verify-project-ground/ground-abutments/okseong-user-files/quantity-*/landing-hero 등)
정본: DOC/04_실행/실행_*_2026-11-09_v0.1.00.md

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
ypshimandClaude Haiku 5.5 committed 2026-11-09 18:01:02 +09:00
1 parent b2bebe47eb
commit fc520ec03d
47 files changed
+2848 -149

No files matched your search

@@ -0,0 +1,664 @@
// Generated by scripts/promote-landing-hero.py from actual model projection.
export const heroCapture = {
"width": 2172,
"height": 870,
"elevation": 0.85,
"along": 0,
"outline": {
"method": "inward-alpha-envelope-quadratic",
"retainedTerrainRatio": 0.9268103107372453,
"addedOutsideSourcePixels": 0
},
"endMarginM": 30,
"cornerRadiusM": 18,
"terrainHalfWidthM": 56,
"roads": [
{
"name": "A1–P1",
"widthM": 8,
"validSections": 81,
"missingSections": 0,
"triangles": 2560,
"visualizationOnly": true
},
{
"name": "P6–A2",
"widthM": 8,
"validSections": 81,
"missingSections": 0,
"triangles": 2560,
"visualizationOnly": true
}
],
"points": [
{
"name": "A1",
"kind": "abutment",
"x": 264.03,
"y": 399.71
},
{
"name": "P1",
"kind": "pier",
"x": 462.89,
"y": 407.62
},
{
"name": "P2",
"kind": "pier",
"x": 712.6,
"y": 401.43
},
{
"name": "P3",
"kind": "pier",
"x": 962.31,
"y": 395.46
},
{
"name": "P4",
"kind": "pier",
"x": 1213.33,
"y": 391.75
},
{
"name": "P5",
"kind": "pier",
"x": 1464.3,
"y": 394.12
},
{
"name": "P6",
"kind": "pier",
"x": 1713.67,
"y": 407.08
},
{
"name": "A2",
"kind": "abutment",
"x": 1911.22,
"y": 410.06
}
],
"terrainFlow": [
{
"x": 264.033,
"y": 448.436
},
{
"x": 275.731,
"y": 448.555
},
{
"x": 287.428,
"y": 448.674
},
{
"x": 299.126,
"y": 449.022
},
{
"x": 310.824,
"y": 448.307
},
{
"x": 322.521,
"y": 447.275
},
{
"x": 334.219,
"y": 446.007
},
{
"x": 345.917,
"y": 445.252
},
{
"x": 357.614,
"y": 444.752
},
{
"x": 369.312,
"y": 443.105
},
{
"x": 381.009,
"y": 441.457
},
{
"x": 392.707,
"y": 439.355
},
{
"x": 404.405,
"y": 437.296
},
{
"x": 416.102,
"y": 435.21
},
{
"x": 427.8,
"y": 432.427
},
{
"x": 439.498,
"y": 429.689
},
{
"x": 451.195,
"y": 426.961
},
{
"x": 462.893,
"y": 424.25
},
{
"x": 474.784,
"y": 421.577
},
{
"x": 486.675,
"y": 419.082
},
{
"x": 498.565,
"y": 416.661
},
{
"x": 510.456,
"y": 414.622
},
{
"x": 522.347,
"y": 415.821
},
{
"x": 534.238,
"y": 416.946
},
{
"x": 546.129,
"y": 419.229
},
{
"x": 558.02,
"y": 423.307
},
{
"x": 569.911,
"y": 427.384
},
{
"x": 581.802,
"y": 431.453
},
{
"x": 593.692,
"y": 435.523
},
{
"x": 605.583,
"y": 439.146
},
{
"x": 617.474,
"y": 440.666
},
{
"x": 629.365,
"y": 441.616
},
{
"x": 641.256,
"y": 441.504
},
{
"x": 653.147,
"y": 441.125
},
{
"x": 665.038,
"y": 440.763
},
{
"x": 676.928,
"y": 440.769
},
{
"x": 688.819,
"y": 440.77
},
{
"x": 700.71,
"y": 440.772
},
{
"x": 712.601,
"y": 440.775
},
{
"x": 724.492,
"y": 440.777
},
{
"x": 736.383,
"y": 440.72
},
{
"x": 748.274,
"y": 440.623
},
{
"x": 760.165,
"y": 440.506
},
{
"x": 772.057,
"y": 440.384
},
{
"x": 783.948,
"y": 440.263
},
{
"x": 795.839,
"y": 440.153
},
{
"x": 807.73,
"y": 440.075
},
{
"x": 819.621,
"y": 439.997
},
{
"x": 831.512,
"y": 439.889
},
{
"x": 843.403,
"y": 439.753
},
{
"x": 855.294,
"y": 439.604
},
{
"x": 867.186,
"y": 439.365
},
{
"x": 879.077,
"y": 439.223
},
{
"x": 890.968,
"y": 439.22
},
{
"x": 902.859,
"y": 439.363
},
{
"x": 914.75,
"y": 439.208
},
{
"x": 926.641,
"y": 438.641
},
{
"x": 938.532,
"y": 438.027
},
{
"x": 950.423,
"y": 437.98
},
{
"x": 962.315,
"y": 439.693
},
{
"x": 974.268,
"y": 441.41
},
{
"x": 986.221,
"y": 444.7
},
{
"x": 998.174,
"y": 448.696
},
{
"x": 1010.127,
"y": 452.659
},
{
"x": 1022.08,
"y": 455.006
},
{
"x": 1034.033,
"y": 457.171
},
{
"x": 1045.986,
"y": 458.569
},
{
"x": 1057.939,
"y": 458.814
},
{
"x": 1069.892,
"y": 459.134
},
{
"x": 1081.845,
"y": 459.621
},
{
"x": 1093.798,
"y": 460.055
},
{
"x": 1105.751,
"y": 460.438
},
{
"x": 1117.704,
"y": 460.085
},
{
"x": 1129.657,
"y": 459.728
},
{
"x": 1141.61,
"y": 459.372
},
{
"x": 1153.563,
"y": 459.015
},
{
"x": 1165.516,
"y": 458.66
},
{
"x": 1177.469,
"y": 457.484
},
{
"x": 1189.422,
"y": 456.209
},
{
"x": 1201.375,
"y": 455.301
},
{
"x": 1213.328,
"y": 455
},
{
"x": 1225.279,
"y": 454.84
},
{
"x": 1237.23,
"y": 454.627
},
{
"x": 1249.181,
"y": 454.385
},
{
"x": 1261.132,
"y": 454.143
},
{
"x": 1273.083,
"y": 453.906
},
{
"x": 1285.034,
"y": 453.693
},
{
"x": 1296.985,
"y": 453.477
},
{
"x": 1308.936,
"y": 453.261
},
{
"x": 1320.887,
"y": 453.048
},
{
"x": 1332.838,
"y": 452.917
},
{
"x": 1344.789,
"y": 452.794
},
{
"x": 1356.74,
"y": 452.12
},
{
"x": 1368.691,
"y": 450.348
},
{
"x": 1380.642,
"y": 448.577
},
{
"x": 1392.593,
"y": 448.16
},
{
"x": 1404.544,
"y": 447.954
},
{
"x": 1416.495,
"y": 447.746
},
{
"x": 1428.446,
"y": 447.807
},
{
"x": 1440.397,
"y": 447.996
},
{
"x": 1452.348,
"y": 446.603
},
{
"x": 1464.299,
"y": 443.94
},
{
"x": 1476.174,
"y": 441.755
},
{
"x": 1488.049,
"y": 441.693
},
{
"x": 1499.924,
"y": 441.701
},
{
"x": 1511.799,
"y": 441.422
},
{
"x": 1523.674,
"y": 440.908
},
{
"x": 1535.549,
"y": 440.562
},
{
"x": 1547.424,
"y": 437.6
},
{
"x": 1559.299,
"y": 434.638
},
{
"x": 1571.173,
"y": 431.58
},
{
"x": 1583.048,
"y": 428.343
},
{
"x": 1594.923,
"y": 425.105
},
{
"x": 1606.798,
"y": 421.806
},
{
"x": 1618.673,
"y": 418.513
},
{
"x": 1630.548,
"y": 416.153
},
{
"x": 1642.423,
"y": 414.67
},
{
"x": 1654.298,
"y": 413.145
},
{
"x": 1666.173,
"y": 414.583
},
{
"x": 1678.048,
"y": 415.925
},
{
"x": 1689.923,
"y": 418.147
},
{
"x": 1701.798,
"y": 421.257
},
{
"x": 1713.672,
"y": 424.538
},
{
"x": 1725.293,
"y": 428.715
},
{
"x": 1736.913,
"y": 432.916
},
{
"x": 1748.534,
"y": 435.788
},
{
"x": 1760.154,
"y": 437.518
},
{
"x": 1771.774,
"y": 439.215
},
{
"x": 1783.395,
"y": 440.178
},
{
"x": 1795.015,
"y": 441.136
},
{
"x": 1806.635,
"y": 442.009
},
{
"x": 1818.256,
"y": 442.838
},
{
"x": 1829.876,
"y": 443.654
},
{
"x": 1841.496,
"y": 444.383
},
{
"x": 1853.117,
"y": 445.119
},
{
"x": 1864.737,
"y": 445.637
},
{
"x": 1876.357,
"y": 446.014
},
{
"x": 1887.978,
"y": 446.453
},
{
"x": 1899.598,
"y": 447.172
},
{
"x": 1911.219,
"y": 447.903
}
],
"camera": {
"position": [
-1142.5706300600066,
1298.0918545724896,
1012.9959736070584
],
"target": [
77.50014114379883,
2.7950143814086914,
99.96070098876953
],
"left": -220.05126480649932,
"right": 219.07033276742936,
"top": 95.74252164639043,
"bottom": -80.1487260006252
},
"piers": 6,
"abutments": 2,
"sourceSha256": "59f2e09500dc55dbd8ca588d99ad8dd8daa92fd55e34b4eeb1daf10a89c3d4c8"
} as const;
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 261 KiB

+25 -13
View File
@@ -1,25 +1,37 @@
/** Decorative geometry uses the PNG's exact 2172×724 pixel coordinate system. */
export const heroMotionMarkup = (imageUrl: string): string => `
<div class="landing-scan-mask" style="mask-image:url('${imageUrl}');-webkit-mask-image:url('${imageUrl}')">
<div class="landing-terrain-grid"></div><div class="landing-scan-beam"></div>
</div>
<svg class="landing-model-motion" viewBox="0 0 2172 724" preserveAspectRatio="xMidYMid meet" aria-hidden="true">
import { heroCapture } from './assets/okseong-hero-capture';
const flowPath = (() => {
let connected = false;
return heroCapture.terrainFlow.map(point => {
if (point === null) { connected = false; return ''; }
const command = connected ? 'L' : 'M'; connected = true;
return `${command}${point.x} ${point.y}`;
}).join(' ');
})();
const frame = {
left: Math.min(...heroCapture.points.map(p=>p.x))-50,
right: Math.max(...heroCapture.points.map(p=>p.x))+50,
top: Math.min(...heroCapture.points.map(p=>p.y))-60,
bottom: Math.max(...heroCapture.points.map(p=>p.y))+70,
};
/** Markers share the exact camera projection used to capture the model. */
export const heroMotionMarkup = (): string => `
<svg class="landing-model-motion" viewBox="0 0 ${heroCapture.width} ${heroCapture.height}" preserveAspectRatio="xMidYMid meet" aria-hidden="true">
<defs>
<filter id="landing-energy-glow" x="-30%" y="-200%" width="160%" height="500%"><feGaussianBlur stdDeviation="3"/></filter>
<linearGradient id="landing-flow-fade"><stop offset="0" stop-color="#087fcb" stop-opacity="0"/><stop offset=".12" stop-color="#087fcb"/><stop offset=".88" stop-color="#087fcb"/><stop offset="1" stop-color="#087fcb" stop-opacity="0"/></linearGradient>
<!-- Trace only the clearly visible pale alignment. Fade the uncertain ends instead of inventing bridge geometry. -->
<path id="landing-alignment-path" pathLength="1000" d="M600 335 C650 341 700 367 750 368.5 S850 370 950 370.5 S1020 379 1050 382 S1120 398 1150 398 S1220 395 1250 395.5 S1320 396 1350 395 S1420 392 1450 390 S1520 384 1550 378.5 S1610 352 1650 350"/>
<linearGradient id="landing-flow-fade"><stop offset="0" stop-color="#ffffff" stop-opacity="0"/><stop offset=".12" stop-color="#ffffff"/><stop offset=".88" stop-color="#ffffff"/><stop offset="1" stop-color="#ffffff" stop-opacity="0"/></linearGradient>
<path id="landing-alignment-path" pathLength="1000" d="${flowPath}"/>
</defs>
<use href="#landing-alignment-path" class="landing-energy-track"/>
<use href="#landing-alignment-path" class="landing-energy-haze" filter="url(#landing-energy-glow)"/>
<use href="#landing-alignment-path" class="landing-energy-flow"/>
${[[546,331],[774,335],[996,344],[1238,346],[1478,334],[1745,332]].map(([x,y],i)=>`
<g class="landing-pier-beacon" style="--beacon-delay:${(i*.72+1.6).toFixed(2)}s" transform="translate(${x} ${y})">
${heroCapture.points.map(({name,x,y},i)=>`
<g class="landing-pier-beacon" data-structure="${name}" style="--beacon-delay:${(i*.72+1.6).toFixed(2)}s" transform="translate(${x} ${y})">
<line x1="0" y1="-31" x2="0" y2="-8"/>
<circle class="landing-pier-pulse" r="11"/><circle class="landing-pier-point" r="3.5"/>
<circle class="landing-pier-spark" cx="8" cy="-15" r="1.5"/>
</g>`).join('')}
<path class="landing-hud-bracket" d="M460 260h-24v22 M1775 260h24v22 M460 485h-24v-22 M1775 485h24v-22"/>
<path class="landing-hud-bracket" d="M${frame.left} ${frame.top}h-24v22 M${frame.right} ${frame.top}h24v22 M${frame.left} ${frame.bottom}h-24v-22 M${frame.right} ${frame.bottom}h24v-22"/>
</svg>`;
export function createHeroMotion(root: HTMLElement): void {
+2 -10
View File
@@ -308,10 +308,7 @@ html:has(.landing-overlay:not([hidden])), body:has(.landing-overlay:not([hidden]
.landing-visual { animation: landing-premium-float 10s ease-in-out infinite; }
.landing-artwork { position: absolute; left: 0; top: 40%; width: 100%; aspect-ratio: 2172 / 724; transform: translateY(-50%); }
.landing-artwork .landing-promo-image { display: block; position: static; width: 100%; height: auto; transform: none; filter: brightness(1.12) contrast(1.04) saturate(1.04); }
.landing-model-motion, .landing-scan-mask { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
.landing-scan-mask { mask-size: 100% 100%; mask-repeat: no-repeat; -webkit-mask-size: 100% 100%; -webkit-mask-repeat: no-repeat; overflow: hidden; }
.landing-terrain-grid { position: absolute; inset: 0; opacity: .11; background-image: linear-gradient(#89eaff80 1px,transparent 1px),linear-gradient(90deg,#89eaff80 1px,transparent 1px); background-size: 30px 24px; transform: perspective(650px) rotateX(48deg) scale(1.6); animation: landing-grid-breathe 9.7s ease-in-out infinite; }
.landing-scan-beam { position: absolute; top: -10%; left: -18%; width: 18%; height: 120%; background: linear-gradient(90deg,transparent,#88efff1a 35%,#c1f8ff66 49%,#78dfff17 65%,transparent); transform: translateX(0); animation: landing-scan-pass 14s ease-in-out 1.8s infinite; }
.landing-model-motion { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
.landing-energy-track { fill: none; stroke: #70dfff; stroke-width: 1.4; opacity: .18; }
.landing-energy-flow, .landing-energy-haze { fill: none; stroke: url(#landing-flow-fade); stroke-width: 2.5; stroke-linecap: round; stroke-dasharray: 90 910; animation: landing-energy-run 8.3s linear 1.5s infinite; opacity: .8; }
.landing-energy-haze { stroke-width: 8; opacity: .48; }
@@ -319,7 +316,6 @@ html:has(.landing-overlay:not([hidden])), body:has(.landing-overlay:not([hidden]
.landing-pier-beacon line { stroke: currentColor; stroke-width: 1; opacity: .42; stroke-dasharray: 3 4; }
.landing-pier-point { fill: #d3fdff; filter: drop-shadow(0 0 5px #62ddff); animation: landing-beacon-point 7.9s ease-in-out var(--beacon-delay) infinite; }
.landing-pier-pulse { fill: none; stroke: #8decff; stroke-width: 1.2; opacity: 0; transform-box: fill-box; transform-origin: center; animation: landing-beacon-pulse 7.9s ease-out var(--beacon-delay) infinite; }
.landing-pier-spark { fill: #c1faff; opacity: 0; animation: landing-beacon-spark 7.9s ease-out var(--beacon-delay) infinite; }
.landing-hud-bracket { fill: none; stroke: #8dcfff; stroke-width: 1; opacity: .28; }
.landing-showcase-label { position: absolute; left: 50%; top: 56%; transform: translateX(-50%); color: #84c4dd; font-size: 9px; letter-spacing: .23em; white-space: nowrap; }
.landing-showcase-label > span { display: block; margin-top: 8px; font-size: 8px; letter-spacing: .06em; color: #729db8; text-align: center; }
@@ -387,19 +383,15 @@ html:has(.landing-overlay:not([hidden])), body:has(.landing-overlay:not([hidden]
.landing-paused .landing-light-stage *, .landing-inactive .landing-light-stage *,
.landing-paused .landing-visual, .landing-inactive .landing-visual,
.landing-paused .landing-model-motion *, .landing-inactive .landing-model-motion *,
.landing-paused .landing-scan-mask *, .landing-inactive .landing-scan-mask *,
.landing-paused .landing-hero::before, .landing-inactive .landing-hero::before { animation-play-state: paused !important; }
@keyframes landing-atmosphere { from { transform: translate3d(-1%,-1%,0) scale(1); opacity: .6; } to { transform: translate3d(2%,1%,0) scale(1.06); opacity: 1; } }
@keyframes landing-glow-breathe { from { opacity: .65; scale: .96; } to { opacity: 1; scale: 1.08; } }
@keyframes landing-title-shimmer { 0%,55% { background-position: 110% 50%; } 85%,100% { background-position: -90% 50%; } }
@keyframes landing-premium-float { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-5px); } }
@keyframes landing-scan-pass { 0%,12% { transform: translateX(0); opacity: 0; } 20% { opacity: .7; } 75% { opacity: .7; } 88%,100% { transform: translateX(740%); opacity: 0; } }
@keyframes landing-grid-breathe { 0%,100% { opacity: .06; } 45% { opacity: .17; } }
@keyframes landing-energy-run { 0% { stroke-dashoffset: 1000; opacity: 0; } 12% { opacity: .8; } 88% { opacity: .8; } 100% { stroke-dashoffset: -1000; opacity: 0; } }
@keyframes landing-beacon-point { 0%,55%,100% { opacity: .35; } 12%,27% { opacity: 1; } }
@keyframes landing-beacon-pulse { 0% { opacity: 0; transform: scale(.4); } 12% { opacity: .85; } 42%,100% { opacity: 0; transform: scale(2.3); } }
@keyframes landing-beacon-spark { 0%,8% { opacity: 0; transform: translateY(0); } 18% { opacity: .8; } 40%,100% { opacity: 0; transform: translateY(-18px); } }
@keyframes landing-guide-enter { from { opacity: 0; translate: 0 8px; } to { opacity: 1; translate: 0 0; } }
@keyframes landing-guide-icon-float { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-3px); } }
@keyframes landing-guide-dot { 0%,100% { opacity: .2; } 50% { opacity: 1; } }
@@ -441,7 +433,7 @@ html:has(.landing-overlay:not([hidden])), body:has(.landing-overlay:not([hidden]
.landing-artwork *, .landing-guide *, .landing-guide[open] { animation: none !important; transition: none !important; }
.landing-title { background: none; color: #f2fbff; -webkit-text-fill-color: #f2fbff; }
.landing-title > span { -webkit-text-fill-color: #7ff1ff; }
.landing-scan-beam, .landing-pier-pulse, .landing-pier-spark, .landing-energy-haze { opacity: 0; }
.landing-pier-pulse, .landing-energy-haze { opacity: 0; }
.landing-energy-flow { stroke-dasharray: none; opacity: .35; }
.landing-light-stage { transform: none; }
}
+5 -4
View File
@@ -3,6 +3,7 @@ import { loadSampleIntoApp } from './sample-loader';
import { createSampleExplorer, sampleExplorerMarkup, type Sample } from './sample-explorer';
import { createLandingGuide } from './landing-guide';
import { createHeroMotion, heroMotionMarkup } from './landing-motion';
import { heroCapture } from './assets/okseong-hero-capture';
import './landing-white.css';
import './landing-typography.css';
import './sample-library-popup.css';
@@ -10,7 +11,7 @@ import { createDrawingPopup, drawingPopupMarkup } from './drawing-popup';
const appRoot = document.querySelector<HTMLDivElement>('.app');
const homeButton = document.querySelector<HTMLButtonElement>('#home-from-app');
const promoImageUrl = new URL('./assets/pierzainer-terrain-promo.png', import.meta.url).href;
const promoImageUrl = new URL('./assets/okseong-model-hero.webp', import.meta.url).href;
const companyLogosUrl = new URL('./assets/family-company-logos.png', import.meta.url).href;
const landingOverlay = document.createElement('div');
@@ -48,9 +49,9 @@ const landingPanelMarkup = `
</div>
<div class="landing-hero-visual" aria-hidden="true">
<div class="landing-visual">
<div class="landing-artwork">
<img class="landing-promo-image" src="${promoImageUrl}" alt="" draggable="false" fetchpriority="high" />
${heroMotionMarkup(promoImageUrl)}
<div class="landing-artwork" style="aspect-ratio:${heroCapture.width}/${heroCapture.height}">
<img class="landing-promo-image" src="${promoImageUrl}" width="${heroCapture.width}" height="${heroCapture.height}" alt="" draggable="false" fetchpriority="high" />
${heroMotionMarkup()}
</div>
</div>
</div>
+9 -10
View File
@@ -1,4 +1,10 @@
/* Premium White UI. Theme selectors never reach the application outside this overlay. */
@media (min-width: 761px) and (max-width: 1400px) {
.landing-white .landing-guide-hint { top: 20px; }
}
@media (max-width: 760px) and (min-height: 760px) {
.landing-white .landing-guide-hint { position: fixed; top: auto; bottom: 16px; }
}
.landing-overlay.landing-white {
--landing-bg: #f8fafc;
--landing-panel: rgba(255, 255, 255, .84);
@@ -85,21 +91,14 @@
pointer-events: none;
}
.landing-white .landing-artwork .landing-promo-image { position: relative; inset: auto; z-index: 1; filter: brightness(1.02) contrast(1.02); }
.landing-white .landing-model-motion, .landing-white .landing-scan-mask { z-index: 2; }
.landing-white .landing-terrain-grid {
background-image: linear-gradient(#167eae47 1px, transparent 1px), linear-gradient(90deg, #167eae47 1px, transparent 1px);
}
.landing-white .landing-scan-beam {
background: linear-gradient(90deg, transparent, #3b9fd31a 36%, #6bc6ed47 49%, #59abd917 64%, transparent);
}
.landing-white .landing-energy-track { stroke: #087fcb; stroke-width: 1.2; opacity: .25; }
.landing-white .landing-model-motion { z-index: 2; }
.landing-white .landing-energy-track { stroke: #ffffff; stroke-width: 1.2; opacity: .16; }
.landing-white .landing-energy-flow { stroke-width: 2; }
.landing-white .landing-energy-haze { stroke-width: 5; opacity: .14; }
.landing-white .landing-energy-haze { stroke-width: 5; opacity: .4; }
.landing-white .landing-pier-beacon { color: #087fcb; }
.landing-white .landing-pier-beacon line { stroke: #167ead; opacity: .58; }
.landing-white .landing-pier-point { fill: #087fcb; stroke: #f6fcff; stroke-width: 1.5; filter: drop-shadow(0 1px 2px #087fcb33); }
.landing-white .landing-pier-pulse { stroke: #087fcb; stroke-width: 1.5; }
.landing-white .landing-pier-spark { fill: #1284bf; }
.landing-white .landing-hud-bracket { stroke: #538bb0; opacity: .35; }
/* White glass surfaces with consistent cobalt actions. */