diff --git a/A00_Common/spreadsheet/spreadsheet_condfmt.ts b/A00_Common/spreadsheet/spreadsheet_condfmt.ts index 4e07e779..625b7d7f 100644 --- a/A00_Common/spreadsheet/spreadsheet_condfmt.ts +++ b/A00_Common/spreadsheet/spreadsheet_condfmt.ts @@ -186,7 +186,7 @@ export function createCondFmtPanel(ctx: SpreadsheetContext): PartHandle { const val2 = createInputField({ label: "위(사이일 때만)" }); const fill = createInputField({ label: "채움색", type: "text", placeholder: "#RRGGBB" }); const fontColor = createInputField({ label: "글자색", type: "text", placeholder: "#RRGGBB" }); - const addBtn = createButton({ label: "규칙 더하기" }); + const addBtn = createButton({ label: "규칙 추가" }); addBtn.addEventListener("click", () => { const 범위 = ctx.selection.범위; diff --git a/A00_Common/spreadsheet/spreadsheet_links.ts b/A00_Common/spreadsheet/spreadsheet_links.ts index 963f0a68..2890bef6 100644 --- a/A00_Common/spreadsheet/spreadsheet_links.ts +++ b/A00_Common/spreadsheet/spreadsheet_links.ts @@ -61,7 +61,7 @@ const TEXT = { unlink: "연결 풀기", remove: "목록에서 빼기", removed: "목록에서 뺌 — 연결도 풀림", - add: "+ 자재 더하기", + add: "+ 자재 추가", search: "자재 이름 · 규격 · 코드", searching: "찾는 중…", none: "맞는 자재 없음", diff --git a/A00_Common/spreadsheet/spreadsheet_names.ts b/A00_Common/spreadsheet/spreadsheet_names.ts index ba7619de..7bd63780 100644 --- a/A00_Common/spreadsheet/spreadsheet_names.ts +++ b/A00_Common/spreadsheet/spreadsheet_names.ts @@ -142,7 +142,7 @@ export function createNameManagerPanel(ctx: SpreadsheetContext): PartHandle { const root = el("div", { className: "ss-names" }); const list = el("ul", { className: "ss-names__list" }); const nameInput = createInputField({ label: "새 이름", placeholder: "단가" }); - const addBtn = createButton({ label: "이름 더하기" }); + const addBtn = createButton({ label: "이름 추가" }); let error: HTMLElement | null = null; const showError = (msg: string | null): void => { diff --git a/A00_Common/spreadsheet/spreadsheet_sort_dialog.ts b/A00_Common/spreadsheet/spreadsheet_sort_dialog.ts index 2c660248..b34f99b5 100644 --- a/A00_Common/spreadsheet/spreadsheet_sort_dialog.ts +++ b/A00_Common/spreadsheet/spreadsheet_sort_dialog.ts @@ -27,7 +27,7 @@ const TEXT = { Column: ["열", "Column"], Asc: ["오름차순", "Ascending"], Desc: ["내림차순", "Descending"], - AddKey: ["기준 더하기", "Add level"], + AddKey: ["기준 추가", "Add level"], RemoveKey: ["빼기", "Remove"], Cancel: ["취소", "Cancel"], Ok: ["정렬", "Sort"], diff --git a/A00_Common/spreadsheet/spreadsheet_text.ts b/A00_Common/spreadsheet/spreadsheet_text.ts index 7207a032..0b097515 100644 --- a/A00_Common/spreadsheet/spreadsheet_text.ts +++ b/A00_Common/spreadsheet/spreadsheet_text.ts @@ -67,7 +67,7 @@ const TEXT = { "이름 골라 쓰거나 엑셀 형식 코드를 바로 침", "Pick a preset or type an Excel format code", ], - SheetAdd: ["시트 더하기", "Add sheet"], + SheetAdd: ["시트 추가", "Add sheet"], SheetRename: ["이름 바꾸기", "Rename"], SheetDelete: ["지우기", "Delete"], SheetDuplicate: ["복사", "Duplicate"], diff --git a/A00_Common/spreadsheet/spreadsheet_validation.ts b/A00_Common/spreadsheet/spreadsheet_validation.ts index 2f7579ce..51b37630 100644 --- a/A00_Common/spreadsheet/spreadsheet_validation.ts +++ b/A00_Common/spreadsheet/spreadsheet_validation.ts @@ -146,7 +146,7 @@ export function createValidationPanel(ctx: SpreadsheetContext): PartHandle { ], }); const message = createInputField({ label: "안내 글(생략 가능)" }); - const addBtn = createButton({ label: "규칙 더하기" }); + const addBtn = createButton({ label: "규칙 추가" }); addBtn.addEventListener("click", () => { const 범위 = ctx.selection.범위; diff --git a/B01_Dashboard/B01_Dashboard_UI_Style.css b/B01_Dashboard/B01_Dashboard_UI_Style.css index 4503d16d..1b81d6a3 100644 --- a/B01_Dashboard/B01_Dashboard_UI_Style.css +++ b/B01_Dashboard/B01_Dashboard_UI_Style.css @@ -268,15 +268,16 @@ inset: 0; z-index: var(--z-modal); display: flex; - align-items: center; + /* 위에서 고정 거리 — 목록 길이가 바뀌어도 창 윗변 · 찾기 칸이 안 움직임 */ + align-items: flex-start; justify-content: center; - padding: var(--spacing-24); + padding: 10vh var(--spacing-24) var(--spacing-24); background: rgba(38, 17, 74, 0.24); } .b01-dashboard__modal-panel { width: min(560px, 100%); - max-height: calc(100vh - 2 * var(--spacing-24)); + max-height: calc(90vh - var(--spacing-24)); overflow-y: auto; /* 패널 끝까지 굴려도 뒤 대시보드로 넘어가지 않게 가둔다 (2026-09-13 사용자 지시). */ overscroll-behavior: contain; diff --git a/B05_Profile/B05_Profile_UI_Style_RouteRecommend.css b/B05_Profile/B05_Profile_UI_Style_RouteRecommend.css index a3895d00..865ca2db 100644 --- a/B05_Profile/B05_Profile_UI_Style_RouteRecommend.css +++ b/B05_Profile/B05_Profile_UI_Style_RouteRecommend.css @@ -16,7 +16,7 @@ .ui-modal.b05-routerec { width: min(960px, 94vw); - max-height: 92vh; + --modal-max: 92vh; font-size: var(--text-body-sm); } diff --git a/M01_MasterData/M01_MasterData_UI_LogicLab_Style.css b/M01_MasterData/M01_MasterData_UI_LogicLab_Style.css index 7be58350..ee46c034 100644 --- a/M01_MasterData/M01_MasterData_UI_LogicLab_Style.css +++ b/M01_MasterData/M01_MasterData_UI_LogicLab_Style.css @@ -159,7 +159,7 @@ /* 복사해서 만들기(3-4) — 별칭 꼴 식 시트 */ .m01copy__sheet { width: min(1040px, 94vw); - max-height: 90vh; + --modal-max: 90vh; } .m01copy__head { diff --git a/M01_MasterData/M01_MasterData_UI_LogicLab_Text.ts b/M01_MasterData/M01_MasterData_UI_LogicLab_Text.ts index 3e6367ee..29a081d9 100644 --- a/M01_MasterData/M01_MasterData_UI_LogicLab_Text.ts +++ b/M01_MasterData/M01_MasterData_UI_LogicLab_Text.ts @@ -94,7 +94,7 @@ const TEXT = { Copy_Alias_Fork: ["같이 복사하기", "Copy along"], Copy_Alias_Forked: ["복사됨", "Copied"], Copy_Lines: ["식 줄 — 고치거나 더함", "Formula lines — edit or add"], - Copy_AddExtra: ["덧줄 더하기", "Add extra line"], + Copy_AddExtra: ["덧줄 추가", "Add extra line"], Copy_ExtraName: ["새 덧줄", "New extra line"], Copy_Save: ["저장(자체 로직)", "Save as mine"], Copy_Cancel: ["취소", "Cancel"], diff --git a/M01_MasterData/M01_MasterData_UI_Logic_Style.css b/M01_MasterData/M01_MasterData_UI_Logic_Style.css index b6cd54c8..d652e256 100644 --- a/M01_MasterData/M01_MasterData_UI_Logic_Style.css +++ b/M01_MasterData/M01_MasterData_UI_Logic_Style.css @@ -313,7 +313,7 @@ input.m01-logic__input { inset: 0; z-index: 1000; display: flex; - align-items: center; + align-items: flex-start; justify-content: center; background-color: rgba(38, 17, 74, 0.35); /* 공용 창 틀과 같이 뒤를 가림 */ } @@ -323,7 +323,9 @@ input.m01-logic__input { flex-direction: column; gap: var(--spacing-8); width: min(720px, 92vw); - max-height: 80vh; + /* 위에서 고정 거리 — 공용 .ui-modal 과 같은 규칙(찾기 칸이 안 움직임) */ + max-height: var(--modal-max, 80vh); + margin-top: calc((100vh - var(--modal-max, 80vh)) / 2); padding: var(--spacing-16); border-radius: var(--radius-xl); border: 1px solid var(--color-border); diff --git a/M01_MasterData/M01_MasterData_UI_Logic_Text.ts b/M01_MasterData/M01_MasterData_UI_Logic_Text.ts index 5ee23361..0f08ee73 100644 --- a/M01_MasterData/M01_MasterData_UI_Logic_Text.ts +++ b/M01_MasterData/M01_MasterData_UI_Logic_Text.ts @@ -55,7 +55,7 @@ const TEXT = { Extra_Title: ["덧줄", "Extra lines"], Rounding: ["끝수", "Rounding"], Formula: ["식", "Formula"], - AddRow: ["줄 더하기", "Add line"], + AddRow: ["줄 추가", "Add line"], DeleteRow: ["줄 지우기", "Delete line"], Calc_Title: ["시험 계산", "Test calculation"], Calc_Run: ["계산", "Calculate"], @@ -124,10 +124,10 @@ const TEXT = { /* ── 상자에서 고치기 ── */ Edit_On: ["고치기", "Edit"], Edit_Off: ["고치기 끝", "Done"], - Edit_AddHo: ["호표 줄 더하기", "Add unit-cost line"], - Edit_AddInput: ["설계 값 더하기", "Add design value"], - Edit_AddMiddle: ["표 찾기 더하기", "Add table lookup"], - Edit_AddExtra: ["덧줄 더하기", "Add extra line"], + Edit_AddHo: ["호표 줄 추가", "Add unit-cost line"], + Edit_AddInput: ["설계 값 추가", "Add design value"], + Edit_AddMiddle: ["표 찾기 추가", "Add table lookup"], + Edit_AddExtra: ["덧줄 추가", "Add extra line"], Edit_Qty: ["수량 식", "Qty formula"], /* ── 멈춘 까닭을 설계자 말로 ── */ Stop_Missing: [ diff --git a/M01_MasterData/M01_MasterData_UI_Style.css b/M01_MasterData/M01_MasterData_UI_Style.css index 61c5c965..f2bd7a23 100644 --- a/M01_MasterData/M01_MasterData_UI_Style.css +++ b/M01_MasterData/M01_MasterData_UI_Style.css @@ -185,7 +185,7 @@ inset: 0; z-index: 1000; display: flex; - align-items: center; + align-items: flex-start; justify-content: center; background: rgba(0, 0, 0, 0.45); } @@ -195,6 +195,7 @@ gap: 8px; width: min(640px, 92vw); max-height: 80vh; + margin-top: 10vh; /* 위에서 고정 거리 — 찾기 칸이 안 움직임 */ padding: 16px; border-radius: 8px; background: var(--color-surface, #fff); diff --git a/M02_MasterTemplete/M02_MasterTemplete_Combo.ts b/M02_MasterTemplete/M02_MasterTemplete_Combo.ts index 39262ef5..c11aa148 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_Combo.ts +++ b/M02_MasterTemplete/M02_MasterTemplete_Combo.ts @@ -36,7 +36,7 @@ const TEXT = { up: "위", down: "아래", remove: "빼기", - add: "로직 더하기", + add: "로직 추가", apply: "적용", close: "닫기", empty: "담은 로직이 없음", diff --git a/M02_MasterTemplete/M02_MasterTemplete_Drawing.css b/M02_MasterTemplete/M02_MasterTemplete_Drawing.css index 8a1b99c8..4a9153a9 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_Drawing.css +++ b/M02_MasterTemplete/M02_MasterTemplete_Drawing.css @@ -81,6 +81,7 @@ .m02-pop > .ui-modal { position: absolute; + margin-top: 0; width: 23rem; max-width: calc(100vw - 16px); box-shadow: 0 4px 16px rgb(0 0 0 / 25%); diff --git a/M02_MasterTemplete/M02_MasterTemplete_Labor_Text.ts b/M02_MasterTemplete/M02_MasterTemplete_Labor_Text.ts index d1fd7e12..962d3eab 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_Labor_Text.ts +++ b/M02_MasterTemplete/M02_MasterTemplete_Labor_Text.ts @@ -8,7 +8,7 @@ import { currentLanguageIndex } from "@ui/ui_template_locale"; const TEXT = { Title: ["일위대가 조합", "Unit-price combination"], Library: ["일위대가 라이브러리", "Unit-price library"], - AddCombo: ["조합 더하기", "Add combination"], + AddCombo: ["조합 추가", "Add combination"], ComboName: ["조합 이름", "Combination name"], ReadOnly: ["읽기만 — 이 층은 고칠 수 없음", "Read only — this layer cannot be edited"], Sub: ["구분", "Category"], @@ -52,7 +52,7 @@ const TEXT = { Up: ["올리기", "Up"], Down: ["내리기", "Down"], Remove: ["빼기", "Remove"], - AddLogic: ["로직 더하기", "Add logic"], + AddLogic: ["로직 추가", "Add logic"], NoLogic: ["담은 로직 없음", "No logics yet"], Open: ["열기", "Open"], Pick_Title: ["로직 고르기", "Pick a logic"], diff --git a/M02_MasterTemplete/M02_MasterTemplete_Materials.ts b/M02_MasterTemplete/M02_MasterTemplete_Materials.ts index 787b0d91..809c92d7 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_Materials.ts +++ b/M02_MasterTemplete/M02_MasterTemplete_Materials.ts @@ -35,7 +35,7 @@ const TEXT = { unit: "단위", cell: "이은 칸", remove: "빼기", - add: "자재 더하기", + add: "자재 추가", apply: "적용", undo: "되돌리기", empty: "담은 자재가 없음", @@ -51,7 +51,7 @@ const TEXT = { askAlone: "이 자재 조합은 모든 프로젝트가 같이 씀 — 고친 내용으로 새 자재 조합을 만들고 이 열만 새 조합을 가리킴 · 같은 내용이면 있는 조합을 씀", failed: "자재 조합을 못 고침 — {value}", - pickTitle: "자재 더하기", + pickTitle: "자재 추가", pickClose: "닫기", search: "자재 이름 · 규격 · 코드", searching: "찾는 중…", diff --git a/M02_MasterTemplete/M02_MasterTemplete_Matset_Text.ts b/M02_MasterTemplete/M02_MasterTemplete_Matset_Text.ts index 541568e8..10bb76b7 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_Matset_Text.ts +++ b/M02_MasterTemplete/M02_MasterTemplete_Matset_Text.ts @@ -36,7 +36,7 @@ const TEXT = { Col_Spec: ["규격", "Spec"], Col_Unit: ["단위", "Unit"], Remove: ["빼기", "Remove"], - AddItem: ["자재 더하기", "Add material"], + AddItem: ["자재 추가", "Add material"], NoItem: ["담은 자재 없음", "No materials yet"], } as const satisfies Record; diff --git a/resources/tester/test_modal_top_and_add_word.py b/resources/tester/test_modal_top_and_add_word.py new file mode 100644 index 00000000..315f9212 --- /dev/null +++ b/resources/tester/test_modal_top_and_add_word.py @@ -0,0 +1,74 @@ +"""모달 위 고정 · 「더하기」 → 「추가」(PLAN 30-1) — 화면 판정은 ORCA. + +· 찾기 있는 모달은 세로 가운데 대신 위에서 고정 거리(목록 길이가 바뀌어도 찾기 칸이 안 움직임) +· 보이는 글에 「더하기」 없음(주석 · 안쪽 명령 이름 `시트더하기` 는 뺌) +""" + +from __future__ import annotations + +import re +import subprocess +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[2] + + +def _text(rel: str) -> str: + return (ROOT / rel).read_text(encoding="utf-8") + + +def _block(css: str, selector: str) -> str: + found = re.search(rf"^{re.escape(selector)} \{{([^}}]*)\}}", css, re.M) + assert found, selector + return found.group(1) + + +def test_공용_모달_위_고정(): + css = _text("ui_template/ui_template_modal.css") + assert "align-items: flex-start;" in _block(css, ".ui-modal__backdrop") + modal = _block(css, ".ui-modal") + assert "max-height: var(--modal-max, 80vh);" in modal + assert "margin-top: calc((100vh - var(--modal-max, 80vh)) / 2);" in modal + # 큰 창은 max-height 대신 --modal-max(위 거리가 함께 맞음) + assert "--modal-max: 94vh;" in _block( + _text("ui_template/ui_template_item_picker.css"), ".ui-modal.ui-item-picker" + ) + assert "--modal-max: 92vh;" in _block( + _text("B05_Profile/B05_Profile_UI_Style_RouteRecommend.css"), ".ui-modal.b05-routerec" + ) + assert "margin-top: 0;" in _block( + _text("M02_MasterTemplete/M02_MasterTemplete_Drawing.css"), ".m02-pop > .ui-modal" + ) + + +def test_따로_둔_모달도_위_고정(): + logic = _text("M01_MasterData/M01_MasterData_UI_Logic_Style.css") + assert "align-items: flex-start;" in _block(logic, ".m01-logic__backdrop") + assert "margin-top: calc((100vh - var(--modal-max, 80vh)) / 2);" in _block( + logic, ".m01-logic__pick" + ) + assert "--modal-max: 90vh;" in _block( + _text("M01_MasterData/M01_MasterData_UI_LogicLab_Style.css"), ".m01copy__sheet" + ) + m01 = _text("M01_MasterData/M01_MasterData_UI_Style.css") + assert "align-items: flex-start;" in _block(m01, ".m01-procure") + assert "margin-top: 10vh;" in _block(m01, ".m01-procure__box") + b01 = _text("B01_Dashboard/B01_Dashboard_UI_Style.css") + assert "align-items: flex-start;" in _block(b01, ".b01-dashboard__modal") + + +def test_보이는_글에_더하기_없음(): + files = subprocess.run( + ["git", "ls-files", "*.ts"], cwd=ROOT, capture_output=True, text=True, encoding="utf-8" + ).stdout.split() + left = [] + for rel in files: + if rel.startswith(("tmp/", "docs/", "resources/")): + continue + for no, line in enumerate(_text(rel).splitlines(), 1): + if line.strip().startswith(("//", "*", "/*")): + continue + for m in re.finditer(r"([\"'`])((?:(?!\1).)*더하기(?:(?!\1).)*)\1", line): + if m.group(2) != "시트더하기": # 안쪽 명령 이름 — 화면에 안 나옴 + left.append(f"{rel}:{no}: {m.group(2)}") + assert not left, left diff --git a/ui_template/sheet/ui_template_sheet_text.ts b/ui_template/sheet/ui_template_sheet_text.ts index 66e7d6fd..16472237 100644 --- a/ui_template/sheet/ui_template_sheet_text.ts +++ b/ui_template/sheet/ui_template_sheet_text.ts @@ -6,7 +6,7 @@ import { currentLanguageIndex } from "@ui/ui_template_locale"; const TEXT = { - Col_Add: ["열 더하기", "Add column"], + Col_Add: ["열 추가", "Add column"], Col_Delete: ["열 지우기", "Delete column"], Col_New: ["새 열", "New column"], Row_Unit: ["단위", "Unit"], @@ -14,7 +14,7 @@ const TEXT = { Row_Drawing: ["도면", "Drawing"], Row_Area: ["면적산출", "Area"], Row_Material: ["재료산출", "Material"], - Add_Suffix: [" 더하기", " +"], + Add_Suffix: [" 추가", " +"], New_Prefix: ["새 ", "New "], Drawing_None: ["없음", "None"], Page: ["쪽", "page"], diff --git a/ui_template/ui_template_item_picker.css b/ui_template/ui_template_item_picker.css index 44852a47..805e228a 100644 --- a/ui_template/ui_template_item_picker.css +++ b/ui_template/ui_template_item_picker.css @@ -2,7 +2,7 @@ /* 공용 모달(.ui-modal)의 폭 · 높이보다 앞서게 — 불러오는 차례에 따라 뒤집히지 않도록 두 클래스 */ .ui-modal.ui-item-picker { width: min(1320px, 96vw); - max-height: 94vh; + --modal-max: 94vh; } .ui-item-picker .ui-modal__body { diff --git a/ui_template/ui_template_locale_b3.ts b/ui_template/ui_template_locale_b3.ts index 6f9404c7..42ffcf80 100644 --- a/ui_template/ui_template_locale_b3.ts +++ b/ui_template/ui_template_locale_b3.ts @@ -94,7 +94,7 @@ export const ui_locales_b3 = { B09_Sheet_Edit: ["편집", "Edit"], B09_Sheet_EditDone: ["편집 끝", "Done"], B09_Sheet_RemoveRow: ["이 줄을 뺌(↺ 로 되돌림)", "Remove this row (undo with ↺)"], - B09_Sheet_AddRow: ["줄 더하기", "Add row"], + B09_Sheet_AddRow: ["줄 추가", "Add row"], B09_Sheet_SearchTitle: [ "노임·자재·중기·일위대가 이름으로 찾기", "Search labor, material, machine or unit price", diff --git a/ui_template/ui_template_locale_m1.ts b/ui_template/ui_template_locale_m1.ts index 5a3e6fad..e7e67786 100644 --- a/ui_template/ui_template_locale_m1.ts +++ b/ui_template/ui_template_locale_m1.ts @@ -17,7 +17,7 @@ export const ui_locales_m1 = { M01_Page: ["쪽", "pages"], M01_Rows: ["줄", "rows"], M01_NoRows: ["줄 없음", "No rows"], - M01_RowAdd: ["줄 더하기", "Add row"], + M01_RowAdd: ["줄 추가", "Add row"], M01_RowDelete: ["지우기", "Delete"], M01_RowRestore: ["되살리기", "Restore"], M01_RowRemove: ["빼기", "Remove"], diff --git a/ui_template/ui_template_locale_m2.ts b/ui_template/ui_template_locale_m2.ts index 047ce548..47931f4d 100644 --- a/ui_template/ui_template_locale_m2.ts +++ b/ui_template/ui_template_locale_m2.ts @@ -111,11 +111,11 @@ export const ui_locales_m2 = { "항목이 없음 — 빈 자리 오른쪽 클릭 또는 층 단추 옆 「+」 로 폴더 · 항목을 더함", "No items — right-click an empty area or use + beside the layer buttons to add", ], - M02_TreeAddTop: ["맨 위에 폴더 · 항목 더하기", "Add folder or item at top"], + M02_TreeAddTop: ["맨 위에 폴더 · 항목 추가", "Add folder or item at top"], M02_TreeEmpty: ["항목이 없음", "No items"], M02_TreeNoHit: ["찾은 항목이 없음", "No matching item"], - M02_TreeAddFolder: ["폴더 더하기", "Add folder"], - M02_TreeAddItem: ["항목 더하기", "Add item"], + M02_TreeAddFolder: ["폴더 추가", "Add folder"], + M02_TreeAddItem: ["항목 추가", "Add item"], M02_TreeRename: ["이름 바꾸기", "Rename"], M02_TreeMove: ["옮기기", "Move"], M02_TreeFolderName: ["폴더 이름", "Folder name"], diff --git a/ui_template/ui_template_modal.css b/ui_template/ui_template_modal.css index 8d777e89..725c285f 100644 --- a/ui_template/ui_template_modal.css +++ b/ui_template/ui_template_modal.css @@ -3,17 +3,20 @@ inset: 0; z-index: 1000; display: flex; - align-items: center; + align-items: flex-start; justify-content: center; background-color: rgba(38, 17, 74, 0.35); /* 공용 확인 창(.ui-confirm)과 같이 뒤를 가림 */ } +/* 위에서 고정 거리 — 최대 높이일 때 가운데 자리에 윗변을 붙이고 아래로만 늘고 줆(찾기 칸이 안 움직임). + 큰 창은 max-height 대신 --modal-max 를 줌. */ .ui-modal { display: flex; flex-direction: column; gap: var(--spacing-8); width: min(720px, 92vw); - max-height: 80vh; + max-height: var(--modal-max, 80vh); + margin-top: calc((100vh - var(--modal-max, 80vh)) / 2); padding: var(--spacing-16); border-radius: var(--radius-xl); border: 1px solid var(--color-border);