Merge remote-tracking branches 'origin/sub_laptop_1', 'origin/sub_laptop_2', 'origin/sub_laptop_3', 'origin/sub_laptop_5' and 'origin/sub_laptop_6' into CODEX
This commit is contained in:
@@ -108,6 +108,14 @@ const PRESERVED_KEYS = [
|
||||
...SPOIL_KEYS,
|
||||
] as const;
|
||||
|
||||
/**
|
||||
* 포장 = 노면공 · 물넘이 범위 안만(138-1) — 측점에 얹힌 제원(`pavement` · `ford_pavement`)이 곧 범위다.
|
||||
* 서버 짝 = `B06_Section_Router_Design.paved_at` · 저장값 · 카드 선택은 쓰지 않는다.
|
||||
*/
|
||||
export function pavedBySpecs(section: Pick<CrossSection, "pavement" | "ford_pavement">): boolean {
|
||||
return Boolean(section.pavement?.length) || Boolean(section.ford_pavement);
|
||||
}
|
||||
|
||||
function preserveUserFields(
|
||||
next: NonNullable<CrossSection["design"]>,
|
||||
previous: CrossSection["design"],
|
||||
@@ -293,7 +301,7 @@ function refreshLocally(input: CrossRefreshInput): number[] | null {
|
||||
ditchType:
|
||||
choice?.ditch_type ??
|
||||
(typeof design.ditch_type === "string" ? design.ditch_type : "standard"),
|
||||
paved: choice?.paved ?? Boolean(design.paved),
|
||||
paved: pavedBySpecs(section),
|
||||
standard,
|
||||
berm: bermAt(section.chainage_m),
|
||||
rockBoundaryOffsetM,
|
||||
|
||||
@@ -226,26 +226,29 @@ def _side_spec(
|
||||
return spec
|
||||
|
||||
|
||||
def _chute(values: dict[str, Any]) -> dict[str, Any] | None:
|
||||
"""도수로 · 돌붙임(90-2) — 첫 상자(`chute_add_{n}_*`) 구조 · 두께(cm → m · 빈 값 30 cm) ·
|
||||
구조를 안 고르면 None. 횡단이 유출 성토사면을 따라 띠로 그림. TS 짝 `chute`."""
|
||||
head = re.compile(r"^chute_add_(\d+)_structure$")
|
||||
def _chute(values: dict[str, Any], head: str = "chute_add") -> dict[str, Any] | None:
|
||||
"""도수로 · 돌붙임(90-2) — 첫 상자(`{head}_{n}_*` · 물넘이 포장 = `ford_chute` · 136-2) 구조 ·
|
||||
두께(cm → m · 빈 값 30 cm) · 구조를 안 고르면 None. 횡단이 유출 성토사면을 따라 띠로 그림.
|
||||
TS 짝 `chute`."""
|
||||
pattern = re.compile(rf"^{head}_(\d+)_structure$")
|
||||
picked = sorted(
|
||||
(int(hit.group(1)), str(value).strip())
|
||||
for key, value in values.items()
|
||||
if (hit := head.match(key)) and str(value or "").strip()
|
||||
if (hit := pattern.match(key)) and str(value or "").strip()
|
||||
)
|
||||
if not picked:
|
||||
return None
|
||||
n, structure = picked[0]
|
||||
cm = _number(values.get(f"chute_add_{n}_thickness"), None)
|
||||
cm = _number(values.get(f"{head}_{n}_thickness"), None)
|
||||
cm = cm if cm and cm > 0 else CHUTE_THICKNESS_CM
|
||||
return {"structure": structure, "thickness_m": round(cm / 100.0, 3)}
|
||||
|
||||
|
||||
def _with_chute(spec: dict[str, Any], values: dict[str, Any]) -> dict[str, Any]:
|
||||
def _with_chute(
|
||||
spec: dict[str, Any], values: dict[str, Any], head: str = "chute_add"
|
||||
) -> dict[str, Any]:
|
||||
"""도수로 · 돌붙임이 있으면 세트에 `chute` 로 실음(90-2)."""
|
||||
chute = _chute(values)
|
||||
chute = _chute(values, head)
|
||||
if chute:
|
||||
spec["chute"] = chute
|
||||
return spec
|
||||
@@ -502,16 +505,20 @@ def _ford_pavement_set(options: dict[str, Any] | None) -> dict[str, Any]:
|
||||
used = degree if degree is not None else fallback
|
||||
slope = round(math.tan(math.radians(used)) * 100.0, 4) if used is not None else legacy
|
||||
span = width if width and width > 0 else FORD_PAVEMENT_DEFAULT_WIDTH_M
|
||||
return {
|
||||
"type": "ford_pavement",
|
||||
"span_m": span,
|
||||
# 노선 중심에서 잰 깊이. 없으면 화면이 파임을 그리지 않는다(수치를 지어내지 않는다).
|
||||
"depth_m": depth if depth and depth > 0 else None,
|
||||
"slope_pct": slope,
|
||||
"defaulted": _defaulted(
|
||||
values, [("ford_width_m", "월류 폭", span), ("ford_slope_deg", "경사각", fallback)]
|
||||
),
|
||||
}
|
||||
return _with_chute(
|
||||
{
|
||||
"type": "ford_pavement",
|
||||
"span_m": span,
|
||||
# 노선 중심에서 잰 깊이. 없으면 화면이 파임을 그리지 않는다(수치를 지어내지 않는다).
|
||||
"depth_m": depth if depth and depth > 0 else None,
|
||||
"slope_pct": slope,
|
||||
"defaulted": _defaulted(
|
||||
values, [("ford_width_m", "월류 폭", span), ("ford_slope_deg", "경사각", fallback)]
|
||||
),
|
||||
},
|
||||
values,
|
||||
"ford_chute",
|
||||
)
|
||||
|
||||
|
||||
def _box_set(options: dict[str, Any] | None) -> dict[str, Any]:
|
||||
|
||||
@@ -44,6 +44,8 @@ from B06_Section.B06_Section_Router_Design import (
|
||||
USER_TOUCHED_KEYS,
|
||||
ford_drop_at,
|
||||
ford_surface_drops,
|
||||
paved_at,
|
||||
pavement_ranges,
|
||||
stored_berm,
|
||||
stored_cut_slope,
|
||||
)
|
||||
@@ -640,7 +642,7 @@ async def compute_cross_section_design(
|
||||
section_mode=request.section_mode,
|
||||
ditch_side=request.ditch_side,
|
||||
ditch_type=request.ditch_type,
|
||||
paved=request.paved,
|
||||
paved=paved_at(request.chainage_m, pavement_ranges(project_root)),
|
||||
standard=request.standard_cross_section,
|
||||
rock_boundary_offset_m=request.rock_boundary_offset_m,
|
||||
two_stage_slope=request.two_stage_slope,
|
||||
|
||||
@@ -110,12 +110,17 @@ def ford_drop_at(chainage_m: float, drops: list[tuple[float, float]]) -> float:
|
||||
return 0.0
|
||||
|
||||
|
||||
def paved_at(chainage_m: float, ranges: list[tuple[float, float]], stored: Any = None) -> bool:
|
||||
"""이 측점을 포장으로 볼 것인가 — 구간 안이면 강제, 밖이면 사용자 저장값(기본 비포장)."""
|
||||
for start, end in ranges:
|
||||
if start - 1e-9 <= chainage_m <= end + 1e-9:
|
||||
return True
|
||||
return bool(stored) if isinstance(stored, bool) else False
|
||||
# 범위 끝 허용오차(m) — 브라우저 짝(`common_util_structure_pavement` · `_culvert_sets`)과 같은 값.
|
||||
_PAVED_EDGE_TOLERANCE_M = 0.02
|
||||
|
||||
|
||||
def paved_at(chainage_m: float, ranges: list[tuple[float, float]]) -> bool:
|
||||
"""이 측점을 포장으로 볼 것인가 — 노면공 · 물넘이 범위 안만 포장, 밖은 비포장(저장값 무시).
|
||||
|
||||
브라우저 짝 = `B06_Section_Cross_Refresh.ts` `pavedBySpecs`(같은 규칙 · 138-1).
|
||||
"""
|
||||
tol = _PAVED_EDGE_TOLERANCE_M
|
||||
return any(start - tol <= chainage_m <= end + tol for start, end in ranges)
|
||||
|
||||
|
||||
# 사용자 조작값 — 다시 계산해도 그대로 승계한다. **이 목록이 서버의 유일한 한 벌이다.**
|
||||
@@ -152,7 +157,6 @@ CARRY_INPUT_KEYS = (
|
||||
"ground_type",
|
||||
"ditch_type",
|
||||
"ditch_choice",
|
||||
"paved",
|
||||
"two_stage_slope",
|
||||
"rock_boundary_offset_m",
|
||||
"cut_slope_ratio_user",
|
||||
@@ -178,23 +182,23 @@ def enforce_pavement_ranges(
|
||||
project_root: Path,
|
||||
standard: dict[str, Any] | None = None,
|
||||
) -> int:
|
||||
"""포장 구간·물넘이 범위 안 측점을 포장으로 맞춘다 — 저장분이 비포장이어도 그렇다.
|
||||
"""포장 = 노면공 · 물넘이 범위 안만 — 저장분과 다르면 켬 · 끔 양쪽으로 맞춘다(138-1).
|
||||
|
||||
사용자가 구간을 지정하면 그 안 횡단도에 **자동 반영**된다(2026-08-28 사용자 확정).
|
||||
범위 밖 옛 `paved=true`(지운 노면공 · 옛 카드 단추)는 비포장으로 되돌린다.
|
||||
포장 여부는 횡단경사·포장층을 바꾸므로 플래그만 갈아 끼우지 않고 다시 계산한다.
|
||||
사용자 조작값(반폭·구조물 조정)은 그대로 승계한다.
|
||||
"""
|
||||
ranges = pavement_ranges(project_root)
|
||||
if not ranges:
|
||||
return 0
|
||||
ford_drops = ford_surface_drops(project_root)
|
||||
changed = 0
|
||||
for section in cross_sections:
|
||||
design = section.get("design")
|
||||
if not isinstance(design, dict) or design.get("paved"):
|
||||
if not isinstance(design, dict):
|
||||
continue
|
||||
chainage = float(section.get("chainage_m", 0.0))
|
||||
if not paved_at(chainage, ranges):
|
||||
wanted = paved_at(chainage, ranges)
|
||||
if bool(design.get("paved")) == wanted:
|
||||
continue
|
||||
try:
|
||||
recomputed = compute_cross_design(
|
||||
@@ -204,7 +208,7 @@ def enforce_pavement_ranges(
|
||||
section_mode=str(design.get("section_mode") or "left_cut"),
|
||||
ditch_side=design.get("ditch_side"),
|
||||
ditch_type=str(design.get("ditch_type") or "standard"),
|
||||
paved=True,
|
||||
paved=wanted,
|
||||
standard=standard,
|
||||
rock_boundary_offset_m=design.get(
|
||||
"rock_boundary_offset_m", STANDARD_ROCK_BOUNDARY_DEFAULT_OFFSET_M
|
||||
@@ -449,7 +453,7 @@ def recompute_designs_for_alignment(
|
||||
section_mode=str(stored.get("section_mode") or modes.get(key, "left_cut")),
|
||||
ditch_side=stored.get("ditch_side"),
|
||||
ditch_type=str(stored.get("ditch_type") or "standard"),
|
||||
paved=paved_at(chainage, paved_ranges, stored.get("paved")),
|
||||
paved=paved_at(chainage, paved_ranges),
|
||||
standard=standard,
|
||||
rock_boundary_offset_m=session_offsets.get(
|
||||
key,
|
||||
@@ -527,7 +531,7 @@ def compute_default_designs(
|
||||
section_mode=default_modes.get(round(chainage_m, 3), "left_cut"),
|
||||
ditch_type=str(user.get("ditch_type") or "standard"),
|
||||
# 포장은 사용자 구간 지정만 켠다 — 경사 제안은 경고로만 남는다(2026-08-28).
|
||||
paved=paved_at(chainage_m, paved_ranges, user.get("paved")),
|
||||
paved=paved_at(chainage_m, paved_ranges),
|
||||
standard=standard,
|
||||
rock_boundary_offset_m=(
|
||||
STANDARD_ROCK_BOUNDARY_DEFAULT_OFFSET_M if rock_offset is None else rock_offset
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* =============================================================================
|
||||
* B06_Section_UI_Cross_Design.ts
|
||||
* 측점 표준횡단 설계 지정 컨트롤(지반유형·단면유형·측구위치·측구형식·포장)과
|
||||
* 측점 표준횡단 설계 지정 컨트롤(지반유형·단면유형·측구위치·측구형식)과
|
||||
* 설계선·암 경계선·포장층 오버레이.
|
||||
*
|
||||
* 카드 헤더 아래에 세그먼트 버튼을 배치하고, 선택이 바뀌면 onChange로 계산을
|
||||
@@ -439,25 +439,13 @@ export function buildDesignControls(
|
||||
!rockCut,
|
||||
L("B06_Design_Disabled_RockCut"),
|
||||
);
|
||||
// 포장 토글: 라벨 삭제, 버튼명 활성="포장"/비활성="비포장"(컬러 유지) — E-7.
|
||||
const paved = toggle("", state.paved, L("B06_Design_Paved_On"), L("B06_Design_Paved_Off"), () => {
|
||||
state.paved = !state.paved;
|
||||
emit();
|
||||
});
|
||||
// B05 법정 경사 분석이 포장을 제안한 측점은 근거 문구를 배지·툴팁으로 표기한다.
|
||||
if (design?.pavement_suggested) {
|
||||
paved.button.title = L("B06_Design_Paved_Suggested");
|
||||
const badge = document.createElement("span");
|
||||
badge.className = "b06-design__paved-badge";
|
||||
badge.textContent = "⚠";
|
||||
badge.title = L("B06_Design_Paved_Suggested");
|
||||
paved.wrap.append(badge);
|
||||
}
|
||||
// 포장 단추는 없다(138-1) — 포장 = 노면공 · 물넘이 범위만(`pavedBySpecs`) · 「포장 권장」은 알림 줄.
|
||||
// `state.paved` 는 저장값 그대로 실어 보낸다(재계산이 범위로 다시 정함).
|
||||
|
||||
// 오버플로 드롭다운(N-2-2): 컨트롤이 카드 폭에서 한 행을 넘치면 뒤쪽부터 우측 "⋯"
|
||||
// 드롭다운(세로 배치)으로 옮겨 다음 행으로 밀리는 것을 막는다. 경사 방향은 항상 인라인,
|
||||
// 이동 우선순위(뒤에서부터): 포장 → 복합경사 → 측구형식 → 측구 토글.
|
||||
const moveable = [ditchToggle.wrap, ditchTypeSeg, twoStage.wrap, paved.wrap];
|
||||
// 이동 우선순위(뒤에서부터): 복합경사 → 측구형식 → 측구 토글.
|
||||
const moveable = [ditchToggle.wrap, ditchTypeSeg, twoStage.wrap];
|
||||
const more = document.createElement("details");
|
||||
more.className = "b06-design__more";
|
||||
const moreSummary = document.createElement("summary");
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* (2026-09-14 브레인 승인 (나)). 판정은 `_Cross_FillSlope_Warn`, 여기는 그리기만.
|
||||
* 측점을 누르면 그 카드로 간다. 경고만 — 구조물을 세우거나 값을 바꾸지 않는다.
|
||||
* ⭐ 2026-09-15 — 그 밑에 사면길이 3m 초과 **소단 검토 정보 한 줄**(경고 모양 아님 · 브레인 ③).
|
||||
* ⭐ 138-1 — 「⚠ 포장 권장 · n측점」 줄(B05 법정 경사 제안 · 포장 범위 밖 측점 · 옛 카드 배지 자리).
|
||||
* ========================================================================== */
|
||||
|
||||
import type { CrossSection } from "./B06_Section_Api_Fetch";
|
||||
@@ -15,6 +16,7 @@ import {
|
||||
type FillSlopeWarning,
|
||||
} from "./B06_Section_UI_Cross_FillSlope_Warn";
|
||||
import { L, stationLabel } from "./B06_Section_UI_Section_Common";
|
||||
import { pavedBySpecs } from "./B06_Section_Cross_Refresh";
|
||||
|
||||
export interface FillSlopeNotice {
|
||||
root: HTMLElement;
|
||||
@@ -46,7 +48,8 @@ function noticeBlock(className: string): {
|
||||
const label = L(side === "left" ? "B06_Design_Ditch_Left" : "B06_Design_Ditch_Right");
|
||||
return `${label} ${open ? "≥" : ""}${lengthM.toFixed(2)}m`;
|
||||
});
|
||||
button.textContent = `${stationLabel(section.chainage_m, stationInterval)} ${parts.join(" · ")}`;
|
||||
button.textContent =
|
||||
`${stationLabel(section.chainage_m, stationInterval)} ${parts.join(" · ")}`.trim();
|
||||
button.dataset.stationId = section.station_id;
|
||||
return button;
|
||||
}),
|
||||
@@ -59,7 +62,9 @@ export function createFillSlopeNotice(onPick: (stationId: string) => void): Fill
|
||||
const root = document.createElement("div");
|
||||
const warn = noticeBlock("b06-section__notice");
|
||||
const info = noticeBlock("b06-section__notice b06-section__notice--info");
|
||||
root.append(warn.root, info.root);
|
||||
const pave = noticeBlock("b06-section__notice");
|
||||
pave.root.title = L("B06_Design_Paved_Suggested");
|
||||
root.append(warn.root, pave.root, info.root);
|
||||
root.addEventListener("click", (event) => {
|
||||
const id = (event.target as HTMLElement).closest("button")?.dataset.stationId;
|
||||
if (id) onPick(id);
|
||||
@@ -73,6 +78,13 @@ export function createFillSlopeNotice(onPick: (stationId: string) => void): Fill
|
||||
fillSlopeWarnings(sections, fillSlopeLengths),
|
||||
stationInterval,
|
||||
);
|
||||
pave.fill(
|
||||
`⚠ ${L("B06_Notice_PaveSuggested")}`,
|
||||
sections
|
||||
.filter((section) => section.design?.pavement_suggested && !pavedBySpecs(section))
|
||||
.map((section) => ({ section, sides: [] })),
|
||||
stationInterval,
|
||||
);
|
||||
info.fill(
|
||||
`ⓘ ${BERM_INFO_TEXT}`,
|
||||
bermReviewStations(sections, slopeRunLengths),
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
import type { CrossDesign } from "./B06_Section_Api_Fetch";
|
||||
import type { OffsetPoint } from "./B06_Section_UI_Cross_Culvert_Types";
|
||||
import { appendChuteBand } from "./B06_Section_UI_Cross_Wall_Hatch";
|
||||
|
||||
const SVG_NS = "http://www.w3.org/2000/svg";
|
||||
/** 빗금 패턴 id는 문서 안에서 유일해야 한다 — 카드마다 하나씩 번호를 준다. */
|
||||
@@ -31,6 +32,8 @@ export interface FordPavementSpec {
|
||||
slope_pct: number | null;
|
||||
/** 정본에 안 적혀 기본값으로 그린 칸 「이름 값」 — 카드 머리에 「기본값(미확정)」. */
|
||||
defaulted?: string[];
|
||||
/** 도수로 · 돌붙임(`ford_chute_{n}_*` · 136-2) — 유출측 성토사면 띠. */
|
||||
chute?: { structure: string; thickness_m: number };
|
||||
}
|
||||
|
||||
interface Edge {
|
||||
@@ -179,9 +182,34 @@ export function appendFordPavementOverlay(
|
||||
"b06-chart__ford-deck",
|
||||
),
|
||||
);
|
||||
if (spec.chute) appendChuteBand(svg, [outletFillLine(design)], spec.chute, x, y);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 유출측(`inflowSign` 반대) 노견 바깥 성토사면 선(136-2) — 설계선을 노견에서 바깥으로 따라
|
||||
* 내려가는 동안만(오르면 절토라 멈춤). 성토가 없으면 빈 선(띠 안 그림).
|
||||
*/
|
||||
export function outletFillLine(design: CrossDesign): OffsetPoint[] {
|
||||
const edges = design.road_edges;
|
||||
if (!edges || !design.design_line?.length) return [];
|
||||
const outward = design.section_mode !== "right_cut" ? -1 : 1; // +offset = 좌측 · 유입 반대
|
||||
const edge =
|
||||
outward > 0
|
||||
? Math.max(edges.left.offset_m, edges.right.offset_m)
|
||||
: Math.min(edges.left.offset_m, edges.right.offset_m);
|
||||
const points = design.design_line
|
||||
.filter((p) => (p.offset_m - edge) * outward >= -1e-6)
|
||||
.sort((a, b) => (a.offset_m - b.offset_m) * outward);
|
||||
const line: OffsetPoint[] = [];
|
||||
for (const p of points) {
|
||||
const last = line[line.length - 1];
|
||||
if (last && p.elevation_m > last.elevation + 1e-6) break;
|
||||
line.push({ offset: p.offset_m, elevation: p.elevation_m });
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
/**
|
||||
* 물넘이 자리 노면 · 측구 설계선을 걷는다(66-2 · 66-3) — 노면은 파인 바닥(실선)과 옛 계획고(점선)가
|
||||
* 대신하고, 측구는 서지 않음. `from` = 설계선 그리기 앞의 자식 수(그 뒤에 붙은 선만 본다).
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* 캐시(`section.culvert`·조정창 제어기)만 고친다 — 영구저장은 [저장]·[확정] 몫이다.
|
||||
* ========================================================================== */
|
||||
|
||||
import { guardSet, pipeDiameterM } from "@util/common_util_culvert_sets";
|
||||
import { chute, guardSet, pipeDiameterM } from "@util/common_util_culvert_sets";
|
||||
import type { DetailPipeInput } from "../B04_PreProcess/B04_PreProcess_Api_Fetch";
|
||||
import { PIPE_MATCH_M } from "../A00_Common/design/design_selection";
|
||||
import type { SectionDetailResponse } from "./B06_Section_Api_Fetch";
|
||||
@@ -51,6 +51,7 @@ export function applyPipeOptionsToCache(
|
||||
patch: Record<string, number | string>,
|
||||
whole = true,
|
||||
): void {
|
||||
syncChute(ctx, chainageM, patch, whole);
|
||||
// 물넘이포장 — 월류 높이 · 경사각을 캐시 제원에 얹음(66-2 · 66-3) · 폭 절반 걸친 옆 카드도
|
||||
const paved = (ctx.detail()?.cross_sections ?? []).filter(
|
||||
(section) =>
|
||||
@@ -163,6 +164,36 @@ export function applyPipeOptionsToCache(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 도수로 · 돌붙임 상자 → 그 관 지점 제원(관 · BOX · 세월교 · 흙막이 · 물넘이 포장)의 `chute` 갈아 끼움
|
||||
* (136-1) · 바뀐 카드만 다시 그림. 일부만 온 되얹기는 상자가 있을 때만 얹음(없음 = 모름).
|
||||
*/
|
||||
function syncChute(
|
||||
ctx: PipeOptionsContext,
|
||||
chainageM: number,
|
||||
patch: Record<string, number | string>,
|
||||
whole: boolean,
|
||||
): void {
|
||||
const found = { chute: chute(patch), pave: chute(patch, "ford_chute") };
|
||||
for (const section of ctx.detail()?.cross_sections ?? []) {
|
||||
let changed = false;
|
||||
for (const [spec, next] of [
|
||||
[section.culvert, found.chute],
|
||||
[section.box, found.chute],
|
||||
[section.ford, found.chute],
|
||||
[section.ford_pavement, found.pave],
|
||||
] as const) {
|
||||
if (!spec || (!whole && !next)) continue;
|
||||
if (Math.abs((spec.chainage_m ?? section.chainage_m) - chainageM) >= PIPE_MATCH_M) continue;
|
||||
if (JSON.stringify(spec.chute ?? null) === JSON.stringify(next)) continue;
|
||||
if (next) spec.chute = { ...next };
|
||||
else delete spec.chute;
|
||||
changed = true;
|
||||
}
|
||||
if (changed) ctx.refreshCard(section.chainage_m);
|
||||
}
|
||||
}
|
||||
|
||||
/** 세월교 · 박스 흙막이 세트를 폼 칸 전부로 다시 지음(73-4) — 흙막이가 없으면 걷음. */
|
||||
function syncBodyGuard(
|
||||
owner: CrossSection,
|
||||
|
||||
@@ -404,13 +404,6 @@
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
/* 포장 제안 배지: B05 법정 경사 분석이 포장을 권장한 측점 표시 */
|
||||
.b06-design__paved-badge {
|
||||
font-size: 0.72rem;
|
||||
color: var(--color-warning);
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
/* 세월교 구체(2026-08-25) — 바닥판은 콘크리트. 측벽은 ㄴ형 집수정 형상을 그대로
|
||||
쓰므로 `.b06-chart__culvert-basin` 색을 공유한다. */
|
||||
.b06-chart__ford-slab {
|
||||
|
||||
Reference in New Issue
Block a user