diff --git a/B05_wf2_Route/B05_wf2_Route_UI_Profile_Edit.ts b/B05_wf2_Route/B05_wf2_Route_UI_Profile_Edit.ts index 05b3d88f..ef4762a9 100644 --- a/B05_wf2_Route/B05_wf2_Route_UI_Profile_Edit.ts +++ b/B05_wf2_Route/B05_wf2_Route_UI_Profile_Edit.ts @@ -19,6 +19,43 @@ import type { import { chainageKey, emptyEdits, hasEdits } from "./B05_wf2_Route_UI_Profile_Alignment"; const DRAFT_KEY_PREFIX = "b05-profile-alignment-draft"; +/** 길게 누르기: 이만큼 유지하면 반복이 시작되고, 그 뒤 초당 10회(0.1m씩)로 이어진다. */ +const HOLD_DELAY_MS = 500; +const HOLD_INTERVAL_MS = 100; + +/** + * 길게 누르는 동안 같은 동작을 반복한다. + * + * 버튼은 편집 한 번마다 다시 그려져 DOM에서 사라지므로, 타이머와 종료 감지를 + * 버튼이 아니라 이 컨트롤러가 window 이벤트로 들고 있어야 반복이 끊기거나 + * 반대로 멈추지 않는 상황이 생기지 않는다. + */ +function createHoldRepeater(): { start: (action: () => void) => void; stop: () => void } { + let delayTimer = 0; + let repeatTimer = 0; + + function stop(): void { + window.clearTimeout(delayTimer); + window.clearInterval(repeatTimer); + delayTimer = 0; + repeatTimer = 0; + window.removeEventListener("pointerup", stop); + window.removeEventListener("pointercancel", stop); + window.removeEventListener("blur", stop); + } + + function start(action: () => void): void { + stop(); + delayTimer = window.setTimeout(() => { + repeatTimer = window.setInterval(action, HOLD_INTERVAL_MS); + }, HOLD_DELAY_MS); + window.addEventListener("pointerup", stop); + window.addEventListener("pointercancel", stop); + window.addEventListener("blur", stop); + } + + return { start, stop }; +} export interface ProfileEditStore { edits(): AlignmentEdits; @@ -117,19 +154,32 @@ export interface EditOverlayOptions { } function overlayButton( + repeater: { start: (action: () => void) => void }, className: string, glyph: string, title: string, - onClick: () => void, + onAction: () => void, ): HTMLButtonElement { const button = document.createElement("button"); button.type = "button"; button.className = `b05-profile-edit__btn ${className}`; button.textContent = glyph; - button.title = title; + button.title = `${title}\n(길게 누르면 0.5초 뒤부터 연속 조정)`; + // 포인터로 누르면 즉시 1회 반응하고, 이어지는 click은 중복이므로 삼킨다. + let swallowClick = false; + button.addEventListener("pointerdown", (event) => { + event.stopPropagation(); + swallowClick = true; + onAction(); + repeater.start(onAction); + }); button.addEventListener("click", (event) => { event.stopPropagation(); - onClick(); + if (swallowClick) { + swallowClick = false; + return; + } + onAction(); }); return button; } @@ -141,18 +191,23 @@ export function createEditOverlay(options: EditOverlayOptions): HTMLElement { layer.className = "b05-profile-edit"; layer.style.width = `${width}px`; + const repeater = createHoldRepeater(); const edited = new Set(Object.keys(alignment.edits.station_offsets)); alignment.stations.forEach((station) => { const left = x(station.chainage_m); const isEdited = edited.has(chainageKey(station.chainage_m)); const label = `${station.chainage_m.toFixed(1)}m 계획고 ${station.plan_elevation_m.toFixed(2)}m`; - const up = overlayButton("is-station is-up", "▲", `${label} — ${step}m 올림`, () => + const up = overlayButton(repeater, "is-station is-up", "▲", `${label} — ${step}m 올림`, () => onStation(station.chainage_m, step), ); up.style.left = `${left - 9}px`; - const down = overlayButton("is-station is-down", "▼", `${label} — ${step}m 내림`, () => - onStation(station.chainage_m, -step), + const down = overlayButton( + repeater, + "is-station is-down", + "▼", + `${label} — ${step}m 내림`, + () => onStation(station.chainage_m, -step), ); down.style.left = `${left - 9}px`; layer.append(up, down); @@ -160,6 +215,7 @@ export function createEditOverlay(options: EditOverlayOptions): HTMLElement { if (!isEdited) return; const offset = alignment.edits.station_offsets[chainageKey(station.chainage_m)]; const reset = overlayButton( + repeater, "is-reset", "↺", `${label} — 자동 선형으로 원복 (현재 ${offset >= 0 ? "+" : ""}${offset.toFixed(2)}m)`, @@ -177,12 +233,16 @@ export function createEditOverlay(options: EditOverlayOptions): HTMLElement { const label = `구간 ${segment.from_m.toFixed(0)}~${segment.to_m.toFixed(0)}m ` + `(구배 ${segment.grade_percent.toFixed(2)}%) 전체 평행이동`; - const up = overlayButton("is-segment is-up", "⇧", `${label} — ${step}m 올림`, () => + const up = overlayButton(repeater, "is-segment is-up", "⇧", `${label} — ${step}m 올림`, () => onSegment(segment, step), ); up.style.left = `${center - 9}px`; - const down = overlayButton("is-segment is-down", "⇩", `${label} — ${step}m 내림`, () => - onSegment(segment, -step), + const down = overlayButton( + repeater, + "is-segment is-down", + "⇩", + `${label} — ${step}m 내림`, + () => onSegment(segment, -step), ); down.style.left = `${center - 9}px`; layer.append(up, down); diff --git a/B05_wf2_Route/B05_wf2_Route_UI_Profile_Panel.ts b/B05_wf2_Route/B05_wf2_Route_UI_Profile_Panel.ts index f2be583a..c4607adf 100644 --- a/B05_wf2_Route/B05_wf2_Route_UI_Profile_Panel.ts +++ b/B05_wf2_Route/B05_wf2_Route_UI_Profile_Panel.ts @@ -41,7 +41,14 @@ import "../B06_wf3_ProfileCross/B06_wf3_ProfileCross_UI_Style.css"; const COLLAPSED_KEY = "b05-route-profile-collapsed"; const HORIZONTAL_SCROLLBAR_HEIGHT = 16; +/** + * 그래프 높이는 패널을 키워도 이 값을 유지한다. 패널을 60vh로 넓힌 목적이 그래프 확대가 + * 아니라 12행 도면 테이블의 가독성 확보이므로, 늘어난 세로 공간은 테이블이 가져간다. + */ +const CHART_HEIGHT = 180; const MIN_CHART_HEIGHT = 120; +/** 12행 × 최소 행높이. 이보다 좁아지면 그래프를 줄여서라도 테이블을 확보한다. */ +const MIN_TABLE_HEIGHT = 168; function readAlignment(data: LongitudinalSection): ProfileAlignment | null { const candidate = data.profile_alignment as ProfileAlignment | undefined; @@ -133,6 +140,7 @@ export function createRouteProfilePanel( let alignment: ProfileAlignment | null = null; let store = createProfileEditStore(null, emptyEdits(), () => rebuild()); let resizeTimer = 0; + let redrawPending = false; let lastWidth = 0; let lastHeight = 0; @@ -205,15 +213,26 @@ export function createRouteProfilePanel( store.replace(next); } + /** + * 편집 후 다시 그린다. 길게 누르기(초당 10회)로 연속 호출되므로 한 프레임에 한 번만 + * 실제 렌더링하도록 모은다. + */ function rebuild(): void { if (base) alignment = buildAlignment(base, store.edits()); - draw(); + if (redrawPending) return; + redrawPending = true; + requestAnimationFrame(() => { + redrawPending = false; + draw(); + }); } function draw(): void { if (!detail || body.clientWidth <= 0 || body.clientHeight <= 0) return; lastWidth = body.clientWidth; lastHeight = body.clientHeight; + // 편집할 때마다 본문을 갈아끼우므로 보고 있던 가로 위치를 잃지 않게 되돌린다. + const scrollLeft = body.scrollLeft; renderBalance(); const longitudinal = detail.longitudinal; @@ -223,7 +242,6 @@ export function createRouteProfilePanel( canvas.className = "b05-profile__canvas"; canvas.style.width = `${width}px`; - // 테이블을 먼저 붙여 실제 높이를 재고, 남는 공간 전부를 그래프에 준다. const x = chainageMapper(longitudinal, width); const table = alignment ? createProfileTable({ @@ -235,10 +253,12 @@ export function createRouteProfilePanel( applyEdits(setCurveRadius(store.edits(), curve, radius ?? 0)), }) : null; - body.replaceChildren(canvas); - if (table) canvas.append(table); - const available = body.clientHeight - HORIZONTAL_SCROLLBAR_HEIGHT - (table?.offsetHeight ?? 0); - const chartHeight = Math.max(MIN_CHART_HEIGHT, available); + + // 그래프는 고정 높이를 지키고 남는 세로 공간은 전부 테이블이 쓴다(행 높이가 늘어난다). + const available = body.clientHeight - HORIZONTAL_SCROLLBAR_HEIGHT; + const chartHeight = table + ? Math.max(MIN_CHART_HEIGHT, Math.min(CHART_HEIGHT, available - MIN_TABLE_HEIGHT)) + : Math.max(MIN_CHART_HEIGHT, available); const chartWrap = document.createElement("div"); chartWrap.className = "b05-profile__chart"; @@ -275,7 +295,10 @@ export function createRouteProfilePanel( }), ); } - canvas.prepend(chartWrap); + canvas.append(chartWrap); + if (table) canvas.append(table); + body.replaceChildren(canvas); + body.scrollLeft = scrollLeft; } const resizeObserver = new ResizeObserver(() => { diff --git a/B05_wf2_Route/B05_wf2_Route_UI_Profile_Table.ts b/B05_wf2_Route/B05_wf2_Route_UI_Profile_Table.ts index 9e8c5c89..fc934ca5 100644 --- a/B05_wf2_Route/B05_wf2_Route_UI_Profile_Table.ts +++ b/B05_wf2_Route/B05_wf2_Route_UI_Profile_Table.ts @@ -1,10 +1,13 @@ /* ============================================================================= * B05_wf2_Route_UI_Profile_Table.ts - * 종단면도 하단 도면 테이블 (구배·절토고·성토고·계획고·지반고·누가거리·거리·측점·곡선). + * 종단면도 하단 도면 테이블 (구배 3행 · 측점값 7행 · 곡선 2행 = 12행). * - * 실무 종단면도 좌측 하단 표를 그대로 옮긴 9행 구성이다. 셀은 종단면도 그래프와 - * **같은 X 매핑**으로 절대 배치되므로 측점 수직선과 정확히 맞물린다. 마지막 곡선 행의 - * R만 입력 가능하고 나머지는 전부 파생값이다. + * 실무 종단면도 좌측 하단 표를 그대로 옮긴 구성이다. 구배와 곡선은 단일값 행이 아니라 + * 도면에서도 여러 줄로 찍히므로 각각 물리적인 행으로 분리했다. + * - 구배: `L=` / `H=` / `S=` 3행 (S행에 변화점 중앙종거를 함께 얹는다) + * - 곡선: `L=` / `R=` 2행 (R만 입력 가능, 고치면 L을 역산한다) + * + * 셀은 종단면도 그래프와 **같은 X 매핑**으로 절대 배치되므로 측점 수직선과 맞물린다. * ========================================================================== */ import type { @@ -23,15 +26,20 @@ export interface ProfileTableOptions { onCurveRadiusChange: (curve: AlignmentCurve, radiusM: number | null) => void; } -interface RowSpec { - key: string; +interface StationRowSpec { label: string; - /** 측점마다 한 칸씩 채우는 행. */ - cell?: (index: number) => string; + cell: (index: number) => string; modifier?: string; } +interface SegmentRowSpec { + label: string; + cell: (segment: AlignmentSegment) => string; +} + const CELL_WIDTH = 56; +/** 구간 블록이 이 폭보다 좁으면 글자가 겹쳐 읽을 수 없으므로 생략한다. */ +const MIN_SEGMENT_WIDTH = 32; function element(tag: string, className: string, text?: string): HTMLElement { const node = document.createElement(tag); @@ -47,118 +55,124 @@ function placeCell(row: HTMLElement, centerX: number, node: HTMLElement): void { row.append(node); } -function buildStationRows(alignment: ProfileAlignment, interval: number): RowSpec[] { +function createRow(className: string, label: string): HTMLElement { + const row = element("div", `b05-profile-table__row ${className}`); + row.append(element("span", "b05-profile-table__label", label)); + return row; +} + +/** 도면의 구배 블록 3행: 구간 연장 / 고저차 / 기울기. */ +const SEGMENT_ROWS: SegmentRowSpec[] = [ + { label: "구배 L", cell: (segment) => `L=${segment.length_m.toFixed(2)}m` }, + { label: "구배 H", cell: (segment) => `H=${segment.height_m.toFixed(2)}m` }, + { label: "구배 S", cell: (segment) => `S=${segment.grade_percent.toFixed(2)}%` }, +]; + +function buildStationRows(alignment: ProfileAlignment, interval: number): StationRowSpec[] { const stations = alignment.stations; return [ { - key: "cut", label: "절토고", modifier: "cut", cell: (index) => (stations[index].cut_m > 0.005 ? stations[index].cut_m.toFixed(2) : ""), }, { - key: "fill", label: "성토고", modifier: "fill", cell: (index) => (stations[index].fill_m > 0.005 ? stations[index].fill_m.toFixed(2) : ""), }, { - key: "plan", label: "계획고", modifier: "plan", cell: (index) => stations[index].plan_elevation_m.toFixed(2), }, - { - key: "ground", - label: "지반고", - cell: (index) => stations[index].ground_elevation_m.toFixed(2), - }, - { - key: "cumulative", - label: "누가거리", - cell: (index) => stations[index].chainage_m.toFixed(2), - }, - { - key: "distance", - label: "거리", - cell: (index) => (index ? stations[index].distance_m.toFixed(2) : ""), - }, - { - key: "station", - label: "측점", - cell: (index) => stationLabel(stations[index].chainage_m, interval), - }, + { label: "지반고", cell: (index) => stations[index].ground_elevation_m.toFixed(2) }, + { label: "누가거리", cell: (index) => stations[index].chainage_m.toFixed(2) }, + { label: "거리", cell: (index) => (index ? stations[index].distance_m.toFixed(2) : "") }, + { label: "측점", cell: (index) => stationLabel(stations[index].chainage_m, interval) }, ]; } /** - * 구배 행: 구간 중앙에 `L / H / S`, 변화점에 중앙종거를 얹는다. - * 도면의 구배 행에 찍히는 소수값(0.56, -0.75 …)이 이 중앙종거다 (|A|·L/8). + * 구배 3행. 각 행은 변화점 사이 구간 전체를 덮는 블록으로 그린다. + * S행에는 변화점 중앙종거를 함께 얹는다 — 도면 구배 행의 소수값(0.56, -0.75 …)이 이 값이다(|A|·L/8). */ -function buildGradeRow(alignment: ProfileAlignment, x: (chainage: number) => number): HTMLElement { - const row = element("div", "b05-profile-table__row b05-profile-table__row--grade"); - row.append(element("span", "b05-profile-table__label", "구배")); - alignment.segments.forEach((segment: AlignmentSegment) => { - const center = (x(segment.from_m) + x(segment.to_m)) / 2; - const span = Math.abs(x(segment.to_m) - x(segment.from_m)); - if (span < 40) return; - const node = element("span", "b05-profile-table__segment"); - node.append( - element("em", "b05-profile-table__segment-line", `L=${segment.length_m.toFixed(2)}m`), - element( - "em", - `b05-profile-table__segment-line is-${segment.height_m >= 0 ? "up" : "down"}`, - `H=${segment.height_m.toFixed(2)}m S=${segment.grade_percent.toFixed(2)}%`, - ), - ); - node.style.left = `${center - span / 2}px`; - node.style.width = `${span}px`; - if (Math.abs(segment.grade_percent) > alignment.policy.max_grade_pct + 1e-6) { - node.classList.add("is-violation"); - node.title = `종단기울기 ${segment.grade_percent.toFixed(2)}%가 기준 ${alignment.policy.max_grade_pct.toFixed(1)}%를 초과합니다.`; +function buildSegmentRows( + alignment: ProfileAlignment, + x: (chainage: number) => number, +): HTMLElement[] { + return SEGMENT_ROWS.map((spec, rowIndex) => { + const row = createRow("b05-profile-table__row--grade", spec.label); + alignment.segments.forEach((segment) => { + const left = x(segment.from_m); + const span = Math.abs(x(segment.to_m) - left); + if (span < MIN_SEGMENT_WIDTH) return; + const node = element("span", "b05-profile-table__segment", spec.cell(segment)); + node.style.left = `${left}px`; + node.style.width = `${span}px`; + if (Math.abs(segment.grade_percent) > alignment.policy.max_grade_pct + 1e-6) { + node.classList.add("is-violation"); + node.title = `종단기울기 ${segment.grade_percent.toFixed(2)}%가 기준 ${alignment.policy.max_grade_pct.toFixed(1)}%를 초과합니다.`; + } + row.append(node); + }); + // 중앙종거는 기울기 변화의 결과이므로 마지막 S행에만 표기한다. + if (rowIndex === SEGMENT_ROWS.length - 1) { + alignment.curves.forEach((curve) => { + const node = element( + "span", + "b05-profile-table__ordinate", + (curve.delta_pct >= 0 ? "" : "-") + curve.middle_ordinate_m.toFixed(2), + ); + node.title = `중앙종거 (대수차 ${curve.delta_pct.toFixed(2)}% × L ${curve.l_m.toFixed(1)}m / 8)`; + placeCell(row, x(curve.chainage_m), node); + }); } - row.append(node); + return row; }); - alignment.curves.forEach((curve) => { - const node = element( - "span", - "b05-profile-table__ordinate", - (curve.delta_pct >= 0 ? "" : "-") + curve.middle_ordinate_m.toFixed(2), - ); - node.title = `중앙종거 (대수차 ${curve.delta_pct.toFixed(2)}% × L ${curve.l_m.toFixed(1)}m / 8)`; - placeCell(row, x(curve.chainage_m), node); - }); - return row; } -/** 곡선 행: 변화점마다 `L=` 표기와 R 입력 칸을 둔다 (R을 고치면 L을 역산). */ -function buildCurveRow(options: ProfileTableOptions): HTMLElement { +function curveTitle(curve: AlignmentCurve): string { + return ( + `K=${curve.k.toFixed(2)} A=${curve.delta_pct.toFixed(2)}%\n` + + `BVC=${curve.bvc_m.toFixed(1)}m (EL ${curve.bvc_elevation_m.toFixed(2)})\n` + + `EVC=${curve.evc_m.toFixed(1)}m (EL ${curve.evc_elevation_m.toFixed(2)})` + + (curve.omit_reason ? `\n${curve.omit_reason}` : "") + ); +} + +/** 곡선 2행: 곡선길이 L(파생) / 반경 R(입력). */ +function buildCurveRows(options: ProfileTableOptions): HTMLElement[] { const { alignment, x, onCurveRadiusChange } = options; - const row = element("div", "b05-profile-table__row b05-profile-table__row--curve"); - row.append(element("span", "b05-profile-table__label", "곡선")); + const lengthRow = createRow("b05-profile-table__row--curve", "곡선 L"); + const radiusRow = createRow("b05-profile-table__row--curve", "곡선 R"); + alignment.curves.forEach((curve) => { - const cell = element("span", "b05-profile-table__curve"); - const length = element("em", "b05-profile-table__curve-length", `L=${curve.l_m.toFixed(2)}`); + const lengthCell = element("span", "b05-profile-table__curve", `L=${curve.l_m.toFixed(2)}`); + lengthCell.title = curveTitle(curve); + + const radiusCell = element("span", "b05-profile-table__curve"); const input = document.createElement("input"); input.type = "number"; input.step = "1"; input.min = "1"; input.className = "b05-profile-table__radius"; input.value = curve.r_m.toFixed(1); - input.title = - `종단곡선 반경 R (m) — 수정하면 L = R × |A| 로 역산합니다.\n` + - `K=${curve.k.toFixed(2)} BVC=${curve.bvc_m.toFixed(1)}m EVC=${curve.evc_m.toFixed(1)}m` + - (curve.omit_reason ? `\n${curve.omit_reason}` : ""); + input.title = `종단곡선 반경 R (m) — 수정하면 L = R × |A| 로 역산합니다.\n${curveTitle(curve)}`; input.addEventListener("change", () => { const parsed = Number.parseFloat(input.value); onCurveRadiusChange(curve, Number.isFinite(parsed) && parsed > 0 ? parsed : null); }); - if (curve.skip_allowed) cell.classList.add("is-optional"); - if (curve.omitted) cell.classList.add("is-omitted"); - cell.append(length, input); - placeCell(row, x(curve.chainage_m), cell); + radiusCell.append(input); + + [lengthCell, radiusCell].forEach((cell) => { + if (curve.skip_allowed) cell.classList.add("is-optional"); + if (curve.omitted) cell.classList.add("is-omitted"); + }); + placeCell(lengthRow, x(curve.chainage_m), lengthCell); + placeCell(radiusRow, x(curve.chainage_m), radiusCell); }); - return row; + return [lengthRow, radiusRow]; } export function createProfileTable(options: ProfileTableOptions): HTMLElement { @@ -166,21 +180,16 @@ export function createProfileTable(options: ProfileTableOptions): HTMLElement { const table = element("div", "b05-profile-table"); table.style.width = `${width}px`; - table.append(buildGradeRow(alignment, x)); + table.append(...buildSegmentRows(alignment, x)); buildStationRows(alignment, stationInterval).forEach((spec) => { - const row = element( - "div", - `b05-profile-table__row${spec.modifier ? ` is-${spec.modifier}` : ""}`, - ); - row.append(element("span", "b05-profile-table__label", spec.label)); + const row = createRow(spec.modifier ? `is-${spec.modifier}` : "", spec.label); alignment.stations.forEach((station, index) => { - const value = spec.cell ? spec.cell(index) : ""; + const value = spec.cell(index); if (!value) return; - const cell = element("span", "b05-profile-table__cell", value); - placeCell(row, x(station.chainage_m), cell); + placeCell(row, x(station.chainage_m), element("span", "b05-profile-table__cell", value)); }); table.append(row); }); - table.append(buildCurveRow(options)); + table.append(...buildCurveRows(options)); return table; } diff --git a/B05_wf2_Route/B05_wf2_Route_UI_Style.css b/B05_wf2_Route/B05_wf2_Route_UI_Style.css index bfd8a5b0..983e5141 100644 --- a/B05_wf2_Route/B05_wf2_Route_UI_Style.css +++ b/B05_wf2_Route/B05_wf2_Route_UI_Style.css @@ -34,12 +34,13 @@ background: var(--color-surface); } -/* 하단 종단 패널: 그래프 + 도면 테이블을 담기 위해 화면 높이의 40%를 차지한다. */ +/* 하단 종단 패널: 그래프 + 12행 도면 테이블을 담기 위해 화면 높이의 60%를 차지한다. + 접기 핸들로 언제든 내릴 수 있어 3D 뷰를 전체 영역으로 볼 수 있다. */ .b05-route-profile { position: relative; display: flex; - flex: 0 0 40vh; - flex: 0 0 40dvh; + flex: 0 0 60vh; + flex: 0 0 60dvh; flex-direction: column; min-height: 0; overflow: visible; @@ -336,7 +337,10 @@ 행 이름표만 sticky로 좌측에 고정되어 가로 스크롤에도 계속 보인다. */ .b05-profile-table { position: relative; - flex: 0 0 auto; + display: flex; + flex: 1 1 auto; + flex-direction: column; + min-height: 0; border-top: 1px solid var(--color-border); color: var(--color-text-body); font-size: 10px; @@ -344,19 +348,20 @@ user-select: none; } +/* 12개 행이 남는 세로 공간을 균등하게 나눠 갖는다 (패널을 키우면 행이 두꺼워진다). */ .b05-profile-table__row { position: relative; - height: 16px; + flex: 1 1 0; + min-height: 14px; border-bottom: 1px solid color-mix(in srgb, var(--color-border) 60%, transparent); white-space: nowrap; } .b05-profile-table__row--grade { - height: 28px; + background: color-mix(in srgb, var(--color-surface) 40%, transparent); } -.b05-profile-table__row--curve { - height: 24px; +.b05-profile-table__row--curve:last-child { border-bottom: 0; } @@ -402,18 +407,13 @@ font-weight: var(--font-weight-medium); } +/* 구간 블록: 변화점 사이 직선 전체를 덮어 어느 구간의 값인지 한눈에 보이게 한다. */ .b05-profile-table__segment { - flex-direction: column; - justify-content: center; - gap: 1px; + box-sizing: border-box; border-left: 1px solid color-mix(in srgb, var(--color-border) 70%, transparent); border-right: 1px solid color-mix(in srgb, var(--color-border) 70%, transparent); } -.b05-profile-table__segment-line { - font-style: normal; -} - .b05-profile-table__segment.is-violation { background: color-mix(in srgb, rgb(220 38 38) 12%, transparent); color: rgb(180 83 9); @@ -426,8 +426,8 @@ } .b05-profile-table__curve { - flex-direction: column; - gap: 1px; + z-index: 2; + background: var(--color-surface-raised); } .b05-profile-table__curve.is-optional { @@ -438,10 +438,6 @@ text-decoration: line-through; } -.b05-profile-table__curve-length { - font-style: normal; -} - .b05-profile-table__radius { box-sizing: border-box; width: 100%;