회기 전 자기점검 게이트 화면 재설계와 시간 만료 상태 대비·오버레이 수정
pre 게이트 카드가 행 높이만큼 늘어나면서 base flex의 align-items:center가 살아남아 3축 블록이 1fr 행 한가운데 떠 있었다. 1920x1080에서 카드의 66.5%가 빈 공간이고 축 위아래로 각 277px의 죽은 여백이 생겼다. 척도 라벨은 0.54rem에 nowrap+ellipsis라 901~1279px에서 최대 12개가 잘렸고, 620px 이하에서는 15개 전부 display:none으로 사라져 숫자만 남았다. 320x568에서는 접힌 mid 전용 40px 고정 행이 게이트에도 걸려 카드가 눌리고 헤더와 축이 겹쳤다. - 게이트 카드는 align-self:center + max-height:100% + 내부 스크롤로 중앙에 서고 높이는 내용이 정한다 - 3열 격자를 축별 세로 스택(질문 한 줄 + 전폭 5칸 척도)으로 바꿔 어느 폭에서도 라벨을 자르거나 숨기지 않는다. word-break:keep-all + overflow-wrap:normal로 어절 경계에서만 줄바꿈한다 - 정보가 없던 3 AXES 배지를 제출 버튼이 왜 잠겨 있는지 답하는 0/3 진행 표시로 교체한다 - 40px 고정 행 규칙을 :not(.sx-alliance-gated)로 접힌 mid 전용으로 되돌린다 - 선택 상태 글자색을 반투명 --surface에서 --text-on-accent로 고친다 시간 만료 상태에서 드러난 네 결함도 함께 닫는다. sx-timebar와 종료 모달 패널은 다크 stage 팔레트 시절 잔재로, 정의된 적 없는 --bg-panel의 하드코딩 다크 폴백 위에 테마 토큰 글자를 얹어 라이트에서 대비가 1.13:1까지 떨어졌다. 의미색 토큰과 불투명 표면으로 되돌렸다. sx-page--active > * 의 position:relative가 같은 특이도로 뒤에 있어 종료 모달의 fixed를 덮어써 오버레이가 1532x32px 띠로 눌렸고, 게이트 행 정의는 :has() 두 개짜리 규칙보다 특이도가 낮아 시간 만료 바가 뜨면 밀려나 빈 트랙이 남았다. 둘 다 선택자를 맞춰 정정했다. 계측: 빈 공간 0.665→0.127, 라벨 잘림 12→0, 숨김 15→0, 최소 탭 46x60, 가로 오버플로 0. 배너 대비 1.13→13.88, 모달 제목 2.68→15.79(다크 13.86/15.18). 검증: typecheck, session-layout+alliance-pulse+layout-visual-gate 31/31, 시간만료 종료 다이얼로그 2/2, pre/mid 잠금 2/2, full-sweep+self-directed 30 pass. 남은 음성 4건은 baseline에서도 동일 실패하는 기존 회귀다.
|
|
@ -225,6 +225,7 @@ export function AllianceCheckpointPrompt({
|
||||||
}
|
}
|
||||||
|
|
||||||
const copy = CHECKPOINT_COPY[checkpoint];
|
const copy = CHECKPOINT_COPY[checkpoint];
|
||||||
|
const chosenCount = DIMENSIONS.filter((dimension) => draft[dimension] != null).length;
|
||||||
|
|
||||||
const expanded = checkpoint === "pre" || midExpanded;
|
const expanded = checkpoint === "pre" || midExpanded;
|
||||||
if (!expanded) {
|
if (!expanded) {
|
||||||
|
|
@ -244,7 +245,13 @@ export function AllianceCheckpointPrompt({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section
|
<section
|
||||||
className={`sx-alliance-checkpoint sx-alliance-checkpoint-slot is-expanded is-${checkpoint}`}
|
className={[
|
||||||
|
"sx-alliance-checkpoint sx-alliance-checkpoint-slot is-expanded",
|
||||||
|
`is-${checkpoint}`,
|
||||||
|
checkpoint === "pre" ? "is-gate" : "",
|
||||||
|
]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(" ")}
|
||||||
aria-labelledby={`${fieldsetId}-title`}
|
aria-labelledby={`${fieldsetId}-title`}
|
||||||
>
|
>
|
||||||
<header className="sx-alliance-checkpoint__head">
|
<header className="sx-alliance-checkpoint__head">
|
||||||
|
|
@ -253,8 +260,19 @@ export function AllianceCheckpointPrompt({
|
||||||
<h2 id={`${fieldsetId}-title`}>{copy.title}</h2>
|
<h2 id={`${fieldsetId}-title`}>{copy.title}</h2>
|
||||||
<p>{copy.body}</p>
|
<p>{copy.body}</p>
|
||||||
</div>
|
</div>
|
||||||
<span className="sx-alliance-checkpoint__step" aria-label="3개 축 평가">
|
{/* 3 AXES 배지는 정보가 없었다. 제출 버튼이 왜 잠겨 있는지 답하는
|
||||||
3 AXES
|
남은 선택 수로 바꾼다. */}
|
||||||
|
<span
|
||||||
|
className={`sx-alliance-checkpoint__progress${
|
||||||
|
chosenCount === DIMENSIONS.length ? " is-done" : ""
|
||||||
|
}`}
|
||||||
|
role="status"
|
||||||
|
aria-live="polite"
|
||||||
|
>
|
||||||
|
<b>
|
||||||
|
{chosenCount}/{DIMENSIONS.length}
|
||||||
|
</b>
|
||||||
|
{chosenCount === DIMENSIONS.length ? "선택 완료" : "축 선택함"}
|
||||||
</span>
|
</span>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,20 @@
|
||||||
|
/* =====================================================================
|
||||||
|
회기 자기점검(Alliance checkpoint)
|
||||||
|
표현은 세 가지다.
|
||||||
|
1) 인라인 스트립 — 로딩 / 실패 / mid 접힘 / 잠금 영수증
|
||||||
|
2) 확장 카드 — mid 펼침. 회기 그리드 한 행 안에 들어가야 하므로 조밀하다.
|
||||||
|
3) 게이트 화면 — pre. 첫 발화 전 유일한 화면이라 카드가 화면 중앙에 선다.
|
||||||
|
철칙: 카드를 화면 높이만큼 늘려 내부에 빈 공간을 만들지 않는다. 5점 척도의
|
||||||
|
한글 라벨은 어느 폭에서도 자르거나 숨기지 않고 줄바꿈으로 살린다.
|
||||||
|
===================================================================== */
|
||||||
|
|
||||||
.sx-alliance-checkpoint {
|
.sx-alliance-checkpoint {
|
||||||
--checkpoint-ink: var(--text-strong, #132822);
|
--checkpoint-ink: var(--text-strong, #132822);
|
||||||
--checkpoint-muted: var(--text-muted, #66736f);
|
--checkpoint-muted: var(--text-muted, #66736f);
|
||||||
--checkpoint-line: color-mix(in srgb, var(--accent, #2f695b) 18%, transparent);
|
--checkpoint-line: color-mix(in srgb, var(--accent, #2f695b) 18%, transparent);
|
||||||
|
--checkpoint-hair: color-mix(in srgb, var(--checkpoint-ink) 12%, transparent);
|
||||||
|
--checkpoint-title: clamp(1.02rem, 1.5vw, 1.24rem);
|
||||||
|
--checkpoint-pad: clamp(16px, 1.6vw, 24px);
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
@ -13,6 +26,8 @@
|
||||||
background: color-mix(in srgb, var(--surface, #fff) 94%, var(--accent, #2f695b) 6%);
|
background: color-mix(in srgb, var(--surface, #fff) 94%, var(--accent, #2f695b) 6%);
|
||||||
border: 1px solid var(--checkpoint-line);
|
border: 1px solid var(--checkpoint-line);
|
||||||
border-radius: 14px;
|
border-radius: 14px;
|
||||||
|
word-break: keep-all;
|
||||||
|
overflow-wrap: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sx-alliance-checkpoint > div:not(.sx-alliance-checkpoint__axes) {
|
.sx-alliance-checkpoint > div:not(.sx-alliance-checkpoint__axes) {
|
||||||
|
|
@ -49,9 +64,11 @@
|
||||||
background: color-mix(in srgb, var(--surface, #fff) 94%, var(--crit-tint, #faeae8) 6%);
|
background: color-mix(in srgb, var(--surface, #fff) 94%, var(--crit-tint, #faeae8) 6%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ── 확장 카드 공통 ───────────────────────────────────────────────── */
|
||||||
|
|
||||||
.sx-alliance-checkpoint.is-expanded {
|
.sx-alliance-checkpoint.is-expanded {
|
||||||
display: block;
|
display: block;
|
||||||
padding: clamp(18px, 2vw, 28px);
|
padding: var(--checkpoint-pad);
|
||||||
border-color: color-mix(in srgb, var(--accent, #2f695b) 28%, transparent);
|
border-color: color-mix(in srgb, var(--accent, #2f695b) 28%, transparent);
|
||||||
box-shadow: 0 18px 50px rgb(18 42 35 / 9%);
|
box-shadow: 0 18px 50px rgb(18 42 35 / 9%);
|
||||||
}
|
}
|
||||||
|
|
@ -64,11 +81,15 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
gap: 22px;
|
gap: 16px 22px;
|
||||||
padding-bottom: 18px;
|
padding-bottom: 14px;
|
||||||
border-bottom: 1px solid var(--checkpoint-line);
|
border-bottom: 1px solid var(--checkpoint-line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sx-alliance-checkpoint__head > div {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.sx-alliance-checkpoint__eyebrow {
|
.sx-alliance-checkpoint__eyebrow {
|
||||||
color: var(--accent, #2f695b);
|
color: var(--accent, #2f695b);
|
||||||
font-size: 0.68rem;
|
font-size: 0.68rem;
|
||||||
|
|
@ -78,97 +99,141 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.sx-alliance-checkpoint__head h2 {
|
.sx-alliance-checkpoint__head h2 {
|
||||||
margin: 6px 0 5px;
|
margin: 5px 0;
|
||||||
font-size: clamp(1.05rem, 1.7vw, 1.34rem);
|
font-size: var(--checkpoint-title);
|
||||||
letter-spacing: -0.035em;
|
line-height: 1.3;
|
||||||
|
letter-spacing: -0.03em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sx-alliance-checkpoint__head p {
|
.sx-alliance-checkpoint__head p {
|
||||||
max-width: 72ch;
|
max-width: 62ch;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
color: var(--checkpoint-muted);
|
color: var(--checkpoint-muted);
|
||||||
font-size: 0.86rem;
|
font-size: 0.86rem;
|
||||||
line-height: 1.55;
|
line-height: 1.55;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sx-alliance-checkpoint__step {
|
/* 남은 선택 수는 장식이 아니라 제출 버튼이 왜 잠겨 있는지에 대한 답이다. */
|
||||||
|
.sx-alliance-checkpoint__progress {
|
||||||
|
display: inline-flex;
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
color: color-mix(in srgb, var(--accent, #2f695b) 74%, var(--checkpoint-ink));
|
align-items: baseline;
|
||||||
font-family: ui-monospace, "SFMono-Regular", Consolas, monospace;
|
gap: 5px;
|
||||||
font-size: 0.68rem;
|
align-self: center;
|
||||||
font-weight: 750;
|
padding: 5px 11px;
|
||||||
letter-spacing: 0.12em;
|
color: var(--checkpoint-muted);
|
||||||
|
font-size: 0.74rem;
|
||||||
|
font-weight: 650;
|
||||||
|
white-space: nowrap;
|
||||||
|
background: color-mix(in srgb, var(--checkpoint-ink) 6%, transparent);
|
||||||
|
border-radius: 999px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sx-alliance-checkpoint__progress b {
|
||||||
|
color: var(--checkpoint-ink);
|
||||||
|
font-size: 0.86rem;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sx-alliance-checkpoint__progress.is-done {
|
||||||
|
color: var(--accent, #2f695b);
|
||||||
|
background: color-mix(in srgb, var(--accent, #2f695b) 13%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sx-alliance-checkpoint__progress.is-done b {
|
||||||
|
color: var(--accent, #2f695b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── 3축 ─────────────────────────────────────────────────────────── */
|
||||||
|
|
||||||
.sx-alliance-checkpoint__axes {
|
.sx-alliance-checkpoint__axes {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
gap: 0;
|
||||||
gap: 1px;
|
margin-top: 2px;
|
||||||
margin-top: 18px;
|
|
||||||
overflow: hidden;
|
|
||||||
background: var(--checkpoint-line);
|
|
||||||
border: 1px solid var(--checkpoint-line);
|
|
||||||
border-radius: 12px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.sx-alliance-checkpoint__axes fieldset {
|
.sx-alliance-checkpoint__axes fieldset {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 16px;
|
padding: 14px 0 15px;
|
||||||
background: var(--surface, #fff);
|
|
||||||
border: 0;
|
border: 0;
|
||||||
|
border-top: 1px solid var(--checkpoint-hair);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sx-alliance-checkpoint__axes fieldset:first-of-type {
|
||||||
|
border-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sx-alliance-checkpoint__axes legend {
|
.sx-alliance-checkpoint__axes legend {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 54px;
|
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sx-alliance-checkpoint__axes legend b,
|
|
||||||
.sx-alliance-checkpoint__axes legend span {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sx-alliance-checkpoint__axes legend b {
|
.sx-alliance-checkpoint__axes legend b {
|
||||||
margin-bottom: 3px;
|
color: var(--checkpoint-ink);
|
||||||
font-size: 0.79rem;
|
font-size: 0.84rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sx-alliance-checkpoint__axes legend span {
|
.sx-alliance-checkpoint__axes legend span {
|
||||||
color: var(--checkpoint-muted);
|
color: var(--checkpoint-muted);
|
||||||
font-size: 0.74rem;
|
font-size: 0.82rem;
|
||||||
line-height: 1.45;
|
line-height: 1.45;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 축 이름과 질문은 넓은 폭에서 한 줄로 붙어 세로를 아낀다. */
|
||||||
|
@media (min-width: 700px) {
|
||||||
|
.sx-alliance-checkpoint__axes legend b {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 699px) {
|
||||||
|
.sx-alliance-checkpoint__axes legend b,
|
||||||
|
.sx-alliance-checkpoint__axes legend span {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sx-alliance-checkpoint__axes legend b {
|
||||||
|
margin-bottom: 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.sx-alliance-checkpoint__scale {
|
.sx-alliance-checkpoint__scale {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(5, minmax(0, 1fr));
|
grid-template-columns: repeat(5, minmax(0, 1fr));
|
||||||
gap: 5px;
|
gap: 6px;
|
||||||
margin-top: 12px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sx-alliance-checkpoint__scale label {
|
.sx-alliance-checkpoint__scale label {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: grid;
|
display: grid;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
place-items: center;
|
min-height: 50px;
|
||||||
padding: 8px 2px 7px;
|
align-content: start;
|
||||||
|
justify-items: center;
|
||||||
|
gap: 2px;
|
||||||
|
padding: 7px 4px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
background: color-mix(in srgb, var(--surface, #fff) 91%, var(--checkpoint-ink) 9%);
|
background: var(--bg-surface-2, #f4f2ee);
|
||||||
border: 1px solid transparent;
|
border: 1px solid var(--checkpoint-hair);
|
||||||
border-radius: 8px;
|
border-radius: 10px;
|
||||||
transition: background 150ms ease, border-color 150ms ease, transform 150ms ease;
|
transition: background 150ms ease, border-color 150ms ease, color 150ms ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sx-alliance-checkpoint__scale label:hover {
|
.sx-alliance-checkpoint__scale label:hover {
|
||||||
transform: translateY(-1px);
|
background: color-mix(in srgb, var(--accent, #2f695b) 8%, var(--bg-surface-2, #f4f2ee));
|
||||||
border-color: color-mix(in srgb, var(--accent, #2f695b) 28%, transparent);
|
border-color: color-mix(in srgb, var(--accent, #2f695b) 42%, transparent);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sx-alliance-checkpoint__scale label.is-selected {
|
.sx-alliance-checkpoint__scale label.is-selected {
|
||||||
color: var(--surface, #fff);
|
color: var(--text-on-accent, #fbfaf8);
|
||||||
|
background: var(--accent, #2f695b);
|
||||||
|
border-color: var(--accent, #2f695b);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sx-alliance-checkpoint__scale label.is-selected:hover {
|
||||||
background: var(--accent, #2f695b);
|
background: var(--accent, #2f695b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -180,42 +245,62 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.sx-alliance-checkpoint__scale label:focus-within {
|
.sx-alliance-checkpoint__scale label:focus-within {
|
||||||
outline: 3px solid color-mix(in srgb, var(--accent, #2f695b) 30%, transparent);
|
outline: 3px solid color-mix(in srgb, var(--accent, #2f695b) 34%, transparent);
|
||||||
outline-offset: 2px;
|
outline-offset: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sx-alliance-checkpoint__scale span {
|
.sx-alliance-checkpoint__scale span {
|
||||||
font-size: 0.81rem;
|
font-size: 0.84rem;
|
||||||
font-weight: 780;
|
font-weight: 780;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
line-height: 1.1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 척도 라벨은 척도의 의미 그 자체다. 자르지도 숨기지도 않고 줄바꿈만 허용한다.
|
||||||
|
단 '보통이/다'처럼 어절 안에서 끊기면 안 되므로 어절 경계만 허용한다. */
|
||||||
.sx-alliance-checkpoint__scale small {
|
.sx-alliance-checkpoint__scale small {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
margin-top: 2px;
|
font-size: 0.68rem;
|
||||||
overflow: hidden;
|
line-height: 1.25;
|
||||||
font-size: 0.54rem;
|
text-align: center;
|
||||||
text-overflow: ellipsis;
|
word-break: keep-all;
|
||||||
white-space: nowrap;
|
overflow-wrap: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fieldset[disabled] .sx-alliance-checkpoint__scale label {
|
||||||
|
cursor: progress;
|
||||||
|
opacity: 0.62;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── 커밋 줄 ─────────────────────────────────────────────────────── */
|
||||||
|
|
||||||
.sx-alliance-checkpoint__commit {
|
.sx-alliance-checkpoint__commit {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
gap: 20px;
|
gap: 14px 20px;
|
||||||
margin-top: 18px;
|
margin-top: 14px;
|
||||||
|
padding-top: 14px;
|
||||||
|
border-top: 1px solid var(--checkpoint-line);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sx-alliance-checkpoint__commit > span {
|
.sx-alliance-checkpoint__commit > span {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: flex-start;
|
||||||
gap: 7px;
|
gap: 7px;
|
||||||
color: var(--checkpoint-muted);
|
color: var(--checkpoint-muted);
|
||||||
font-size: 0.74rem;
|
font-size: 0.74rem;
|
||||||
|
line-height: 1.45;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sx-alliance-checkpoint__commit > span svg {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
margin-top: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sx-alliance-checkpoint__commit > div {
|
.sx-alliance-checkpoint__commit > div {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex: 0 0 auto;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -239,16 +324,84 @@
|
||||||
50% { opacity: 0.42; transform: scale(0.72); }
|
50% { opacity: 0.42; transform: scale(0.72); }
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 900px) {
|
/* ── 인라인 mid 카드: 회기 그리드 한 행 안에 머무르도록 압축 ──────── */
|
||||||
.sx-alliance-checkpoint__axes {
|
|
||||||
grid-template-columns: 1fr;
|
.sx-alliance-checkpoint.is-mid.is-expanded {
|
||||||
|
--checkpoint-pad: clamp(14px, 1.2vw, 18px);
|
||||||
|
--checkpoint-title: 1.02rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sx-alliance-checkpoint.is-mid.is-expanded .sx-alliance-checkpoint__head p {
|
||||||
|
font-size: 0.82rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sx-alliance-checkpoint.is-mid.is-expanded .sx-alliance-checkpoint__axes fieldset {
|
||||||
|
padding: 11px 0 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sx-alliance-checkpoint.is-mid.is-expanded .sx-alliance-checkpoint__scale label {
|
||||||
|
min-height: 44px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1080px) {
|
||||||
|
/* mid는 축어록을 밀어내지 않도록 넓은 화면에서만 3열로 눕힌다. */
|
||||||
|
.sx-alliance-checkpoint.is-mid.is-expanded .sx-alliance-checkpoint__axes {
|
||||||
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
|
column-gap: 22px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sx-alliance-checkpoint__axes legend {
|
.sx-alliance-checkpoint.is-mid.is-expanded .sx-alliance-checkpoint__axes fieldset {
|
||||||
min-height: 0;
|
padding: 12px 0 0;
|
||||||
|
border-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sx-alliance-checkpoint.is-mid.is-expanded .sx-alliance-checkpoint__axes legend b,
|
||||||
|
.sx-alliance-checkpoint.is-mid.is-expanded .sx-alliance-checkpoint__axes legend span {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sx-alliance-checkpoint.is-mid.is-expanded .sx-alliance-checkpoint__axes legend b {
|
||||||
|
margin: 0 0 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sx-alliance-checkpoint.is-mid.is-expanded .sx-alliance-checkpoint__axes legend span {
|
||||||
|
min-height: 2.9em;
|
||||||
|
font-size: 0.76rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ── pre 게이트 화면: 카드가 화면 중앙에 서고 높이는 내용이 정한다 ── */
|
||||||
|
|
||||||
|
.sx-alliance-checkpoint.is-gate {
|
||||||
|
--checkpoint-title: clamp(1.22rem, 2.1vw, 1.62rem);
|
||||||
|
--checkpoint-pad: clamp(20px, 2.2vw, 30px);
|
||||||
|
/* 카드 폭은 5칸 척도가 자연스럽게 146px 안팎이 되는 지점에서 멈춘다.
|
||||||
|
더 넓히면 라벨이 빈 상자 안에 뜨고, 제목·질문·척도의 오른쪽 끝이 어긋난다. */
|
||||||
|
width: min(820px, calc(100% - 24px));
|
||||||
|
}
|
||||||
|
|
||||||
|
.sx-alliance-checkpoint.is-gate .sx-alliance-checkpoint__head {
|
||||||
|
padding-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sx-alliance-checkpoint.is-gate .sx-alliance-checkpoint__head p {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sx-alliance-checkpoint.is-gate .sx-alliance-checkpoint__axes fieldset {
|
||||||
|
padding: 16px 0 17px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sx-alliance-checkpoint.is-gate .sx-alliance-checkpoint__scale label {
|
||||||
|
min-height: 56px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sx-alliance-checkpoint.is-gate .sx-alliance-checkpoint__scale small {
|
||||||
|
font-size: 0.72rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── 좁은 화면 ───────────────────────────────────────────────────── */
|
||||||
|
|
||||||
@media (max-width: 620px) {
|
@media (max-width: 620px) {
|
||||||
.sx-alliance-checkpoint {
|
.sx-alliance-checkpoint {
|
||||||
width: calc(100% - 20px);
|
width: calc(100% - 20px);
|
||||||
|
|
@ -261,10 +414,21 @@
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sx-alliance-checkpoint__head,
|
/* 좁은 화면에서 진행 칩이 제목 위로 올라오면 위계가 뒤집힌다.
|
||||||
.sx-alliance-checkpoint__commit {
|
제목과 설명을 먼저 읽히고 칩은 첫 축 바로 앞에 둔다. */
|
||||||
|
.sx-alliance-checkpoint__head {
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sx-alliance-checkpoint__progress {
|
||||||
|
align-self: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sx-alliance-checkpoint__commit {
|
||||||
|
align-items: stretch;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sx-alliance-checkpoint__commit > div {
|
.sx-alliance-checkpoint__commit > div {
|
||||||
|
|
@ -274,10 +438,54 @@
|
||||||
|
|
||||||
.sx-alliance-checkpoint__commit button {
|
.sx-alliance-checkpoint__commit button {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
min-height: 46px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sx-alliance-checkpoint__scale {
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sx-alliance-checkpoint__scale label {
|
||||||
|
padding: 6px 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sx-alliance-checkpoint__scale small {
|
.sx-alliance-checkpoint__scale small {
|
||||||
display: none;
|
font-size: 0.63rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 380px) {
|
||||||
|
.sx-alliance-checkpoint__scale {
|
||||||
|
gap: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sx-alliance-checkpoint__scale label {
|
||||||
|
padding: 6px 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sx-alliance-checkpoint__scale small {
|
||||||
|
font-size: 0.57rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 저높이 폰에서는 게이트 카드가 화면을 넘긴다. 카드 안에서만 스크롤시키고
|
||||||
|
장식 여백을 걷어 다섯 칸 척도와 제출 버튼을 계속 만질 수 있게 둔다. */
|
||||||
|
@media (max-height: 660px) {
|
||||||
|
.sx-alliance-checkpoint.is-gate {
|
||||||
|
--checkpoint-title: 1.08rem;
|
||||||
|
--checkpoint-pad: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sx-alliance-checkpoint.is-gate .sx-alliance-checkpoint__head p {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sx-alliance-checkpoint.is-gate .sx-alliance-checkpoint__axes fieldset {
|
||||||
|
padding: 11px 0 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sx-alliance-checkpoint.is-gate .sx-alliance-checkpoint__scale label {
|
||||||
|
min-height: 46px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -62,29 +62,35 @@
|
||||||
.sx-page--active:has(.sx-timebar) {
|
.sx-page--active:has(.sx-timebar) {
|
||||||
grid-template-rows: min-content min-content min-content minmax(0, 1fr) min-content;
|
grid-template-rows: min-content min-content min-content minmax(0, 1fr) min-content;
|
||||||
}
|
}
|
||||||
.sx-page--active.sx-alliance-gated {
|
/* 게이트 상태의 행 정의는 아래 :has() 두 개짜리 규칙(0,3,0)보다 특이도가 낮아
|
||||||
|
시간 만료 바가 뜨는 순간 밀려났다. 그러면 숨긴 축어록·컨트롤바의 빈 트랙이
|
||||||
|
그대로 남아 화면 절반이 비어 보인다. 같은 :has()를 붙여 항상 이기게 한다. */
|
||||||
|
.sx-page--active.sx-alliance-gated,
|
||||||
|
.sx-page--active.sx-alliance-gated:has(> .sx-alliance-checkpoint-slot) {
|
||||||
grid-template-rows: min-content minmax(0, 1fr);
|
grid-template-rows: min-content minmax(0, 1fr);
|
||||||
}
|
}
|
||||||
|
.sx-page--active.sx-alliance-gated:has(> .sx-timebar):has(> .sx-alliance-checkpoint-slot) {
|
||||||
|
grid-template-rows: min-content min-content minmax(0, 1fr);
|
||||||
|
}
|
||||||
.sx-page--active.sx-alliance-gated .sx-grid {
|
.sx-page--active.sx-alliance-gated .sx-grid {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
.sx-page--active.sx-alliance-gated .sx-controlbar {
|
.sx-page--active.sx-alliance-gated .sx-controlbar {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
/* 게이트 카드는 행 높이만큼 늘리지 않는다. 예전에는 stretch + 1fr 축 행이라
|
||||||
|
1920×1080에서 카드의 66%가 빈 공간이었고 축 블록이 허공에 떠 보였다.
|
||||||
|
이제 카드는 내용 높이로 화면 중앙에 서고, 넘칠 때만 카드 안에서 스크롤한다. */
|
||||||
.sx-page--active.sx-alliance-gated .sx-alliance-checkpoint {
|
.sx-page--active.sx-alliance-gated .sx-alliance-checkpoint {
|
||||||
height: 100%;
|
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
margin-top: 0;
|
max-height: 100%;
|
||||||
overflow: hidden;
|
margin: 0 auto;
|
||||||
align-self: stretch;
|
overflow-y: auto;
|
||||||
|
overscroll-behavior: contain;
|
||||||
|
align-self: center;
|
||||||
}
|
}
|
||||||
.sx-page--active.sx-alliance-gated .sx-alliance-checkpoint.is-expanded {
|
.sx-page--active.sx-alliance-gated .sx-alliance-checkpoint.is-expanded {
|
||||||
display: grid;
|
display: block;
|
||||||
grid-template-rows: auto minmax(0, 1fr) auto auto;
|
|
||||||
}
|
|
||||||
.sx-page--active.sx-alliance-gated .sx-alliance-checkpoint__axes {
|
|
||||||
align-content: start;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
}
|
||||||
[data-theme="light"] .sx-page--active {
|
[data-theme="light"] .sx-page--active {
|
||||||
color-scheme: light;
|
color-scheme: light;
|
||||||
|
|
@ -2556,15 +2562,16 @@
|
||||||
background: rgba(3, 9, 8, 0.62);
|
background: rgba(3, 9, 8, 0.62);
|
||||||
backdrop-filter: blur(8px);
|
backdrop-filter: blur(8px);
|
||||||
}
|
}
|
||||||
|
/* 패널도 다크 stage 잔재였다. 배경만 하드코딩 다크였고 글자는 테마 토큰이라
|
||||||
|
라이트에서 제목 2.68:1 · 본문 1.14:1로 사실상 보이지 않았다. 불투명 토큰
|
||||||
|
표면으로 되돌린다(스크림 위라 유리 표면 대신 불투명 --bg-app 사용). */
|
||||||
.sx-end-dialog__panel {
|
.sx-end-dialog__panel {
|
||||||
width: min(100%, 420px);
|
width: min(100%, 420px);
|
||||||
padding: var(--sp-5);
|
padding: var(--sp-5);
|
||||||
border: 1px solid rgba(203, 227, 220, 0.16);
|
border: 1px solid var(--glass-border);
|
||||||
border-radius: var(--radius-lg);
|
border-radius: var(--radius-lg);
|
||||||
background:
|
background: var(--glass-specular), var(--bg-app);
|
||||||
linear-gradient(180deg, rgba(24, 38, 35, 0.98), rgba(13, 24, 21, 0.98)),
|
box-shadow: 0 26px 70px rgba(0, 0, 0, 0.34);
|
||||||
var(--asset-warm-elements) center / cover no-repeat;
|
|
||||||
box-shadow: 0 26px 70px rgba(0, 0, 0, 0.42);
|
|
||||||
color: var(--text-body);
|
color: var(--text-body);
|
||||||
}
|
}
|
||||||
.sx-end-dialog__head {
|
.sx-end-dialog__head {
|
||||||
|
|
@ -2580,8 +2587,8 @@
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
background: rgba(216, 100, 89, 0.15);
|
background: var(--crit-tint);
|
||||||
color: #f1b2aa;
|
color: var(--crit-text);
|
||||||
}
|
}
|
||||||
.sx-end-dialog h2 {
|
.sx-end-dialog h2 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
@ -2613,17 +2620,17 @@
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
.sx-end-dialog__secondary {
|
.sx-end-dialog__secondary {
|
||||||
border: 1px solid rgba(203, 227, 220, 0.16);
|
border: 1px solid var(--border-subtle, var(--hair));
|
||||||
background: rgba(255, 255, 255, 0.06);
|
background: var(--bg-surface-2);
|
||||||
color: var(--text-body);
|
color: var(--text-body);
|
||||||
}
|
}
|
||||||
.sx-end-dialog__danger {
|
.sx-end-dialog__danger {
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
background: var(--crit-solid);
|
background: var(--crit-solid);
|
||||||
color: #fff;
|
color: var(--text-on-accent);
|
||||||
}
|
}
|
||||||
.sx-end-dialog__secondary:hover:not(:disabled) {
|
.sx-end-dialog__secondary:hover:not(:disabled) {
|
||||||
background: rgba(255, 255, 255, 0.1);
|
background: var(--bg-tint);
|
||||||
color: var(--text-strong);
|
color: var(--text-strong);
|
||||||
}
|
}
|
||||||
.sx-end-dialog__danger:hover:not(:disabled) {
|
.sx-end-dialog__danger:hover:not(:disabled) {
|
||||||
|
|
@ -4554,6 +4561,10 @@
|
||||||
.sx-page--active:has(> .sx-timebar) {
|
.sx-page--active:has(> .sx-timebar) {
|
||||||
grid-template-rows: min-content min-content minmax(0, 1fr) min-content;
|
grid-template-rows: min-content min-content minmax(0, 1fr) min-content;
|
||||||
}
|
}
|
||||||
|
/* 이 바는 세션이 다크 stage 팔레트를 강제하던 시절의 잔재였다. --bg-panel은
|
||||||
|
어디에도 정의된 적이 없어 항상 하드코딩 다크 폴백으로 칠해졌고, 글자만
|
||||||
|
테마 토큰을 따라가 라이트에서 제목 1.13:1 · 설명 1.76:1까지 떨어졌다.
|
||||||
|
두 값 모두 의미색 토큰으로 되돌려 두 테마에서 같은 위계를 갖게 한다. */
|
||||||
.sx-timebar {
|
.sx-timebar {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
@ -4562,11 +4573,9 @@
|
||||||
margin: 0 0 10px;
|
margin: 0 0 10px;
|
||||||
padding: 9px 14px;
|
padding: 9px 14px;
|
||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
border: 1px solid rgba(228, 179, 99, 0.5);
|
border: 1px solid color-mix(in srgb, var(--warn-solid) 46%, transparent);
|
||||||
background:
|
background: var(--warn-tint);
|
||||||
linear-gradient(0deg, rgba(228, 179, 99, 0.12), rgba(228, 179, 99, 0.12)),
|
color: var(--text-strong);
|
||||||
var(--bg-panel, rgba(20, 26, 24, 0.92));
|
|
||||||
color: var(--text-strong, #eef4f2);
|
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
.sx-timebar b {
|
.sx-timebar b {
|
||||||
|
|
@ -4574,7 +4583,7 @@
|
||||||
letter-spacing: -0.01em;
|
letter-spacing: -0.01em;
|
||||||
}
|
}
|
||||||
.sx-timebar__desc {
|
.sx-timebar__desc {
|
||||||
color: var(--text-body, rgba(238, 244, 242, 0.74));
|
color: var(--text-body);
|
||||||
font-weight: 560;
|
font-weight: 560;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
overflow-wrap: anywhere;
|
overflow-wrap: anywhere;
|
||||||
|
|
@ -4583,33 +4592,31 @@
|
||||||
width: 8px;
|
width: 8px;
|
||||||
height: 8px;
|
height: 8px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: #e4b363;
|
background: var(--warn-solid);
|
||||||
box-shadow: 0 0 0 0 rgba(228, 179, 99, 0.5);
|
box-shadow: 0 0 0 0 color-mix(in srgb, var(--warn-solid) 50%, transparent);
|
||||||
animation: sx-timebar-pulse 1.8s ease-out infinite;
|
animation: sx-timebar-pulse 1.8s ease-out infinite;
|
||||||
}
|
}
|
||||||
.sx-timebar.is-over {
|
.sx-timebar.is-over {
|
||||||
border-color: rgba(224, 122, 95, 0.55);
|
border-color: color-mix(in srgb, var(--crit-solid) 48%, transparent);
|
||||||
background:
|
background: var(--crit-tint);
|
||||||
linear-gradient(0deg, rgba(224, 122, 95, 0.14), rgba(224, 122, 95, 0.14)),
|
|
||||||
var(--bg-panel, rgba(20, 26, 24, 0.92));
|
|
||||||
}
|
}
|
||||||
.sx-timebar.is-over .sx-timebar__dot {
|
.sx-timebar.is-over .sx-timebar__dot {
|
||||||
background: #e07a5f;
|
background: var(--crit-solid);
|
||||||
box-shadow: 0 0 0 0 rgba(224, 122, 95, 0.5);
|
box-shadow: 0 0 0 0 color-mix(in srgb, var(--crit-solid) 50%, transparent);
|
||||||
}
|
}
|
||||||
.sx-timebar__end {
|
.sx-timebar__end {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
padding: 6px 12px;
|
padding: 6px 12px;
|
||||||
border-radius: calc(var(--radius-sm) - 1px);
|
border-radius: calc(var(--radius-sm) - 1px);
|
||||||
border: 1px solid rgba(224, 122, 95, 0.6);
|
border: 1px solid color-mix(in srgb, var(--crit-solid) 58%, transparent);
|
||||||
background: rgba(224, 122, 95, 0.16);
|
background: color-mix(in srgb, var(--crit-solid) 12%, transparent);
|
||||||
color: inherit;
|
color: var(--crit-text);
|
||||||
font-size: 12.5px;
|
font-size: 12.5px;
|
||||||
font-weight: 740;
|
font-weight: 740;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
.sx-timebar__end:hover {
|
.sx-timebar__end:hover {
|
||||||
background: rgba(224, 122, 95, 0.26);
|
background: color-mix(in srgb, var(--crit-solid) 22%, transparent);
|
||||||
}
|
}
|
||||||
@keyframes sx-timebar-pulse {
|
@keyframes sx-timebar-pulse {
|
||||||
0% { box-shadow: 0 0 0 0 rgba(228, 179, 99, 0.45); }
|
0% { box-shadow: 0 0 0 0 rgba(228, 179, 99, 0.45); }
|
||||||
|
|
@ -4811,9 +4818,13 @@
|
||||||
.sx-page--active:has(> .sx-timebar):has(> .sx-alliance-checkpoint-slot) {
|
.sx-page--active:has(> .sx-timebar):has(> .sx-alliance-checkpoint-slot) {
|
||||||
grid-template-rows: min-content min-content min-content minmax(0, 1fr) min-content;
|
grid-template-rows: min-content min-content min-content minmax(0, 1fr) min-content;
|
||||||
}
|
}
|
||||||
.sx-page--active.sx-alliance-gated {
|
.sx-page--active.sx-alliance-gated,
|
||||||
|
.sx-page--active.sx-alliance-gated:has(> .sx-alliance-checkpoint-slot) {
|
||||||
grid-template-rows: min-content minmax(0, 1fr);
|
grid-template-rows: min-content minmax(0, 1fr);
|
||||||
}
|
}
|
||||||
|
.sx-page--active.sx-alliance-gated:has(> .sx-timebar):has(> .sx-alliance-checkpoint-slot) {
|
||||||
|
grid-template-rows: min-content min-content minmax(0, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
@media (min-width: 1280px) {
|
@media (min-width: 1280px) {
|
||||||
.sx-page--active:has(> .sx-alliance-checkpoint-slot) {
|
.sx-page--active:has(> .sx-alliance-checkpoint-slot) {
|
||||||
|
|
@ -4822,9 +4833,13 @@
|
||||||
.sx-page--active:has(> .sx-timebar):has(> .sx-alliance-checkpoint-slot) {
|
.sx-page--active:has(> .sx-timebar):has(> .sx-alliance-checkpoint-slot) {
|
||||||
grid-template-rows: 80px min-content min-content minmax(0, 1fr) 116px;
|
grid-template-rows: 80px min-content min-content minmax(0, 1fr) 116px;
|
||||||
}
|
}
|
||||||
.sx-page--active.sx-alliance-gated {
|
.sx-page--active.sx-alliance-gated,
|
||||||
|
.sx-page--active.sx-alliance-gated:has(> .sx-alliance-checkpoint-slot) {
|
||||||
grid-template-rows: 80px minmax(0, 1fr);
|
grid-template-rows: 80px minmax(0, 1fr);
|
||||||
}
|
}
|
||||||
|
.sx-page--active.sx-alliance-gated:has(> .sx-timebar):has(> .sx-alliance-checkpoint-slot) {
|
||||||
|
grid-template-rows: 80px min-content minmax(0, 1fr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 1280px) and (max-height: 820px) {
|
@media (min-width: 1280px) and (max-height: 820px) {
|
||||||
|
|
@ -4834,15 +4849,21 @@
|
||||||
.sx-page--active:has(> .sx-timebar):has(> .sx-alliance-checkpoint-slot) {
|
.sx-page--active:has(> .sx-timebar):has(> .sx-alliance-checkpoint-slot) {
|
||||||
grid-template-rows: 52px min-content min-content minmax(0, 1fr) 82px;
|
grid-template-rows: 52px min-content min-content minmax(0, 1fr) 82px;
|
||||||
}
|
}
|
||||||
.sx-page--active.sx-alliance-gated {
|
.sx-page--active.sx-alliance-gated,
|
||||||
|
.sx-page--active.sx-alliance-gated:has(> .sx-alliance-checkpoint-slot) {
|
||||||
grid-template-rows: 52px minmax(0, 1fr);
|
grid-template-rows: 52px minmax(0, 1fr);
|
||||||
}
|
}
|
||||||
|
.sx-page--active.sx-alliance-gated:has(> .sx-timebar):has(> .sx-alliance-checkpoint-slot) {
|
||||||
|
grid-template-rows: 52px min-content minmax(0, 1fr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 320×568 같은 저높이 폰에서는 mid 자기점검의 설명/버튼 줄바꿈만으로
|
/* 320×568 같은 저높이 폰에서는 mid 자기점검의 설명/버튼 줄바꿈만으로
|
||||||
축어록 행이 사라진다. G1 상태는 그대로 두고 collapsed 표현만 한 줄로 압축한다. */
|
축어록 행이 사라진다. G1 상태는 그대로 두고 collapsed 표현만 한 줄로 압축한다. */
|
||||||
@media (max-width: 420px) and (max-height: 620px) {
|
@media (max-width: 420px) and (max-height: 620px) {
|
||||||
.sx-page--active:has(> .sx-alliance-checkpoint-slot) {
|
/* 40px 고정 행은 접힌 mid 스트립 전용이다. pre 게이트에서는 이 카드가
|
||||||
|
화면의 전부라, 같은 규칙이 걸리면 카드가 40px로 눌려 헤더와 축이 겹쳤다. */
|
||||||
|
.sx-page--active:not(.sx-alliance-gated):has(> .sx-alliance-checkpoint-slot) {
|
||||||
grid-template-rows: min-content 40px minmax(0, 1fr) min-content;
|
grid-template-rows: min-content 40px minmax(0, 1fr) min-content;
|
||||||
}
|
}
|
||||||
.sx-page--active > .sx-alliance-checkpoint.is-collapsed {
|
.sx-page--active > .sx-alliance-checkpoint.is-collapsed {
|
||||||
|
|
@ -4945,7 +4966,10 @@
|
||||||
background-position: -132px 33%, -148px 88%;
|
background-position: -132px 33%, -148px 88%;
|
||||||
transform: scaleX(-1);
|
transform: scaleX(-1);
|
||||||
}
|
}
|
||||||
.sx-page--active > * {
|
/* 보태니컬 ::before/::after 위로 본문을 올리는 규칙이다. 다만 종료 모달은
|
||||||
|
같은 직계 자식이라 이 규칙이 position:fixed를 relative로 덮어썼고, 오버레이가
|
||||||
|
화면을 덮는 대신 그리드 한 칸(1532×32px)으로 눌려 들어갔다. 오버레이는 제외한다. */
|
||||||
|
.sx-page--active > *:not(.sx-end-dialog) {
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -635,6 +635,7 @@
|
||||||
<p class="dg-note">180차 적용(2026-08-07): current source release-only SHA <code>6030a677af7e…c611</code>를 격리 NAS <code>vignette-preview-20260807</code>에 승격했다. 최초 전체 gate는 108개 중 5개를 fixture drift 2종과 runtime-critical <code>shell.css</code> 분류 누락으로 차단해 원격을 바꾸지 않았고, G5→G4 무호출·StrictMode retry·learner 모바일 4열 packaging을 focused 6/6과 manifest 회귀로 고정했다. 최종 candidate browser E2E <code>108/108</code>, postdeploy 실제 SSE→DB review <code>32.437s</code>, health <code>3/3</code>, OpenAPI 126·auth 401·G0~G8 route를 통과했다. API/Web 이미지는 <code>52e0…8b2d</code>/<code>6fdb…f215</code>이며 named DB volume과 배포 전 dump를 보존했다. 이 승격은 current runtime 증거이고 G8 receipt-bound 실제 image rollback은 별도 GATE다.</p>
|
<p class="dg-note">180차 적용(2026-08-07): current source release-only SHA <code>6030a677af7e…c611</code>를 격리 NAS <code>vignette-preview-20260807</code>에 승격했다. 최초 전체 gate는 108개 중 5개를 fixture drift 2종과 runtime-critical <code>shell.css</code> 분류 누락으로 차단해 원격을 바꾸지 않았고, G5→G4 무호출·StrictMode retry·learner 모바일 4열 packaging을 focused 6/6과 manifest 회귀로 고정했다. 최종 candidate browser E2E <code>108/108</code>, postdeploy 실제 SSE→DB review <code>32.437s</code>, health <code>3/3</code>, OpenAPI 126·auth 401·G0~G8 route를 통과했다. API/Web 이미지는 <code>52e0…8b2d</code>/<code>6fdb…f215</code>이며 named DB volume과 배포 전 dump를 보존했다. 이 승격은 current runtime 증거이고 G8 receipt-bound 실제 image rollback은 별도 GATE다.</p>
|
||||||
<p class="dg-note">181차 적용(2026-08-07): <b>G8의 마지막 게이트인 실제 receipt-bound image rollback을 격리 NAS에서 실행</b>해 DONE으로 승격했다. 먼저 성립 불가능한 Gate6 계약을 정정했다. 감사 대상 current API 이미지 자체가 <code>com.docker.compose.project=vignette-preview-20260807</code>·<code>service=api</code> image label을 갖고 있어 "helper의 compose label 0개"는 감사되지 않은 다른 이미지를 쓰지 않는 한 성립하지 않는다. 그래서 계약을 <b>key 부재가 아니라 소속</b>으로 바꿔 <code>scripts/launch-nas-preview-g8-helpers.py</code>에 고정했다. image 상속 label을 baseline으로 읽고, container의 모든 compose label이 baseline과 같거나 선언된 격리 override인지 검사하며, 최종 project는 <code>vignette-g8-helper-*</code>·service는 <code>g8-rollback-*</code>이어야 하고, <code>docker run</code> argv에 target project/service label을 주입하면 fake-runner 테스트가 먼저 깨진다(<code>37/37</code>). 실행은 executor <code>127.0.0.1:18149</code>(host network·read-only rootfs·cap-drop ALL·no-new-privileges·uid1028/gid100·group101)와 별도 lifecycle의 control-plane API <code>127.0.0.1:8018</code>로 구성했다. 계획했던 Windows SSH 터널은 NAS sshd가 <code>administratively prohibited</code>로 direct-tcpip를 거부해 사용할 수 없었고, sshd 설정을 바꾸는 대신 같은 격리 계약의 NAS-side probe 컨테이너에서 실행해 control-plane(8018)·preview(8088) origin 분리를 그대로 유지했다. 결과는 <code>rollback-old</code> receipt <code>nas-g8-723eeef2…</code>, <code>restore-current</code> receipt <code>nas-g8-2738846c…</code>로 실제 이미지가 <code>52e0/6fdb</code>→<code>79ec/c530</code>→<code>52e0/6fdb</code>로 왕복했고, release gate·approval 2회 멱등, DB <code>audit.ci_lifecycle_event</code> <code>rollback/executed</code> 2건과 receipt id 결속, silent auto-promotion 0, HMAC journal 6-record 체인 검증, health 3/3·OpenAPI 126·auth 401·Web 200, helper 0·listener 0 복귀와 비밀 env 파기를 확인했다. <code>down</code>·<code>volume rm</code>·<code>prune</code>은 실행하지 않았고 공개 런타임은 건드리지 않았다. G7은 Deepgram live·인증 WSS·명시 동의 물리 마이크 50분·독립 라벨 human pack이 남아 <b>외부 GATE</b>로 유지한다.</p>
|
<p class="dg-note">181차 적용(2026-08-07): <b>G8의 마지막 게이트인 실제 receipt-bound image rollback을 격리 NAS에서 실행</b>해 DONE으로 승격했다. 먼저 성립 불가능한 Gate6 계약을 정정했다. 감사 대상 current API 이미지 자체가 <code>com.docker.compose.project=vignette-preview-20260807</code>·<code>service=api</code> image label을 갖고 있어 "helper의 compose label 0개"는 감사되지 않은 다른 이미지를 쓰지 않는 한 성립하지 않는다. 그래서 계약을 <b>key 부재가 아니라 소속</b>으로 바꿔 <code>scripts/launch-nas-preview-g8-helpers.py</code>에 고정했다. image 상속 label을 baseline으로 읽고, container의 모든 compose label이 baseline과 같거나 선언된 격리 override인지 검사하며, 최종 project는 <code>vignette-g8-helper-*</code>·service는 <code>g8-rollback-*</code>이어야 하고, <code>docker run</code> argv에 target project/service label을 주입하면 fake-runner 테스트가 먼저 깨진다(<code>37/37</code>). 실행은 executor <code>127.0.0.1:18149</code>(host network·read-only rootfs·cap-drop ALL·no-new-privileges·uid1028/gid100·group101)와 별도 lifecycle의 control-plane API <code>127.0.0.1:8018</code>로 구성했다. 계획했던 Windows SSH 터널은 NAS sshd가 <code>administratively prohibited</code>로 direct-tcpip를 거부해 사용할 수 없었고, sshd 설정을 바꾸는 대신 같은 격리 계약의 NAS-side probe 컨테이너에서 실행해 control-plane(8018)·preview(8088) origin 분리를 그대로 유지했다. 결과는 <code>rollback-old</code> receipt <code>nas-g8-723eeef2…</code>, <code>restore-current</code> receipt <code>nas-g8-2738846c…</code>로 실제 이미지가 <code>52e0/6fdb</code>→<code>79ec/c530</code>→<code>52e0/6fdb</code>로 왕복했고, release gate·approval 2회 멱등, DB <code>audit.ci_lifecycle_event</code> <code>rollback/executed</code> 2건과 receipt id 결속, silent auto-promotion 0, HMAC journal 6-record 체인 검증, health 3/3·OpenAPI 126·auth 401·Web 200, helper 0·listener 0 복귀와 비밀 env 파기를 확인했다. <code>down</code>·<code>volume rm</code>·<code>prune</code>은 실행하지 않았고 공개 런타임은 건드리지 않았다. G7은 Deepgram live·인증 WSS·명시 동의 물리 마이크 50분·독립 라벨 human pack이 남아 <b>외부 GATE</b>로 유지한다.</p>
|
||||||
<p class="dg-note">182차 적용(2026-08-08): <b>회기 전 자기점검(pre alliance gate) 화면을 데스크톱·모바일 전 폭에서 다시 짰다.</b> 계측이 먼저였다. gate 상태에서 카드가 <code>align-self:stretch</code> + <code>grid-template-rows: auto minmax(0,1fr) auto auto</code>인데 base flex의 <code>align-items:center</code>가 살아 있어 3축 블록이 1fr 행 한가운데 떠 있었다. 1920×1080 기준 카드의 <b>66.5%가 빈 공간</b>이고 축 위·아래로 각각 <code>277px</code>의 죽은 여백이 생겼다. 척도 라벨은 <code>0.54rem</code>+<code>nowrap</code>+ellipsis라 901~1279px에서 최대 <b>12개가 "전허 아…"</b>로 잘렸고, ≤620px에서는 <code>display:none</code>으로 <b>15개 전부 사라져</b> 숫자 1~5만 남았다. 320×568에서는 <code>@media (max-width:420px) and (max-height:620px)</code>의 collapsed 전용 <code>40px</code> 고정 행이 gate에도 걸려 카드가 <b>40px로 눌리고 헤더와 축이 겹쳐</b> 화면이 사실상 깨져 있었다. 수정은 네 가지다. (1) gate 카드는 행 높이만큼 늘리지 않고 <code>align-self:center</code>+<code>max-height:100%</code>+내부 스크롤로 화면 중앙에 서게 했다. (2) 3열 격자를 버리고 축마다 <b>질문 한 줄 + 전폭 5칸 척도</b> 세로 스택으로 바꿔 어느 폭에서도 라벨을 자르거나 숨기지 않는다(<code>word-break:keep-all</code>+<code>overflow-wrap:normal</code>로 어절 경계만 줄바꿈). (3) 정보가 없던 <code>3 AXES</code> 모노 배지를 제출 버튼이 왜 잠겨 있는지 답하는 <code>0/3 축 선택함</code> 진행 표시(<code>aria-live</code>)로 교체했다. (4) 40px 행 규칙에 <code>:not(.sx-alliance-gated)</code>를 걸어 collapsed mid 전용으로 되돌렸다. 선택 상태 글자색도 반투명 <code>--surface</code>에서 <code>--text-on-accent</code>로 고쳤다(라이트 4.85:1 · 다크 6.36:1, AA). 15개 뷰포트 재계측 결과 빈 공간 비율 <b>0.665→0.127</b>(1920), 라벨 잘림 <b>12→0</b>, 숨김 <b>15→0</b>, 최소 탭 타깃 <b>38×38→46×60</b>, 문서 가로 오버플로 전 구간 0이고, 320×568에서는 카드 내부 스크롤로 제출까지 도달해 실제 잠금·발화 입력 활성까지 확인했다. 인라인 mid 카드는 ≥1080px에서만 3열로 눕혀 축어록을 밀지 않게 두었고 높이는 오히려 <b>약 55px 줄었다</b>. 무회귀: <code>typecheck</code> 통과, <code>session-layout</code> <b>8/8</b>, <code>alliance-pulse</code> <b>8/8</b>, pre/mid 잠금 E2E <b>2/2</b>, <code>layout-visual-gate</code> 22/23(실패 1건 <code>settings</code>는 변경 전 baseline에서도 동일 실패로 확인된 기존 회귀). session-mvp 음성 4건 실패도 baseline 동일이라 이 변경과 무관하다.</p>
|
<p class="dg-note">182차 적용(2026-08-08): <b>회기 전 자기점검(pre alliance gate) 화면을 데스크톱·모바일 전 폭에서 다시 짰다.</b> 계측이 먼저였다. gate 상태에서 카드가 <code>align-self:stretch</code> + <code>grid-template-rows: auto minmax(0,1fr) auto auto</code>인데 base flex의 <code>align-items:center</code>가 살아 있어 3축 블록이 1fr 행 한가운데 떠 있었다. 1920×1080 기준 카드의 <b>66.5%가 빈 공간</b>이고 축 위·아래로 각각 <code>277px</code>의 죽은 여백이 생겼다. 척도 라벨은 <code>0.54rem</code>+<code>nowrap</code>+ellipsis라 901~1279px에서 최대 <b>12개가 "전허 아…"</b>로 잘렸고, ≤620px에서는 <code>display:none</code>으로 <b>15개 전부 사라져</b> 숫자 1~5만 남았다. 320×568에서는 <code>@media (max-width:420px) and (max-height:620px)</code>의 collapsed 전용 <code>40px</code> 고정 행이 gate에도 걸려 카드가 <b>40px로 눌리고 헤더와 축이 겹쳐</b> 화면이 사실상 깨져 있었다. 수정은 네 가지다. (1) gate 카드는 행 높이만큼 늘리지 않고 <code>align-self:center</code>+<code>max-height:100%</code>+내부 스크롤로 화면 중앙에 서게 했다. (2) 3열 격자를 버리고 축마다 <b>질문 한 줄 + 전폭 5칸 척도</b> 세로 스택으로 바꿔 어느 폭에서도 라벨을 자르거나 숨기지 않는다(<code>word-break:keep-all</code>+<code>overflow-wrap:normal</code>로 어절 경계만 줄바꿈). (3) 정보가 없던 <code>3 AXES</code> 모노 배지를 제출 버튼이 왜 잠겨 있는지 답하는 <code>0/3 축 선택함</code> 진행 표시(<code>aria-live</code>)로 교체했다. (4) 40px 행 규칙에 <code>:not(.sx-alliance-gated)</code>를 걸어 collapsed mid 전용으로 되돌렸다. 선택 상태 글자색도 반투명 <code>--surface</code>에서 <code>--text-on-accent</code>로 고쳤다(라이트 4.85:1 · 다크 6.36:1, AA). 15개 뷰포트 재계측 결과 빈 공간 비율 <b>0.665→0.127</b>(1920), 라벨 잘림 <b>12→0</b>, 숨김 <b>15→0</b>, 최소 탭 타깃 <b>38×38→46×60</b>, 문서 가로 오버플로 전 구간 0이고, 320×568에서는 카드 내부 스크롤로 제출까지 도달해 실제 잠금·발화 입력 활성까지 확인했다. 인라인 mid 카드는 ≥1080px에서만 3열로 눕혀 축어록을 밀지 않게 두었고 높이는 오히려 <b>약 55px 줄었다</b>. 무회귀: <code>typecheck</code> 통과, <code>session-layout</code> <b>8/8</b>, <code>alliance-pulse</code> <b>8/8</b>, pre/mid 잠금 E2E <b>2/2</b>, <code>layout-visual-gate</code> 22/23(실패 1건 <code>settings</code>는 변경 전 baseline에서도 동일 실패로 확인된 기존 회귀). session-mvp 음성 4건 실패도 baseline 동일이라 이 변경과 무관하다.</p>
|
||||||
|
<p class="dg-note">183차 적용(2026-08-08): 182차 뒤 소유자 스크린샷에서 <b>시간 만료 상태의 게이트 화면</b>이 여전히 깨져 있어 네 개의 별개 결함을 계측으로 특정하고 닫았다. (1) <code>.sx-timebar</code>는 세션이 다크 stage 팔레트를 강제하던 시절의 잔재로, 배경이 <b>어디에도 정의된 적 없는</b> <code>--bg-panel</code>의 하드코딩 다크 폴백으로 칠해지는데 글자만 테마 토큰을 따라가 라이트에서 제목 <b>1.13:1</b>·설명 <b>1.76:1</b>이었다. 의미색 토큰(<code>--warn-*</code>/<code>--crit-*</code>)으로 되돌려 <b>13.88:1</b>·<b>6.94:1</b>(다크 13.86·9.30)로 올렸다. (2) 같은 이유로 <code>.sx-end-dialog__panel</code>이 어두운 gradient 위 어두운 글자라 제목 <b>2.68:1</b>·본문 <b>1.14:1</b>로 사실상 보이지 않았다. 불투명 <code>--bg-app</code> 표면으로 바꿔 <b>15.79:1</b>·<b>5.19:1</b>(다크 15.18·6.52)이 됐다. (3) 보태니컬 장식 위로 본문을 올리는 <code>.sx-page--active > *{position:relative}</code>가 같은 특이도(0,1,0)로 <b>뒤에 있어</b> 종료 모달의 <code>position:fixed</code>를 덮어썼다. 오버레이가 화면을 덮는 대신 그리드 한 칸(<code>1532×32px</code>)으로 눌려 들어갔다. <code>:not(.sx-end-dialog)</code>로 제외해 <code>fixed</code> <b>1600×900 뷰포트 전체</b>를 회복했다. (4) 게이트 행 정의 <code>.sx-page--active.sx-alliance-gated</code>(0,2,0)가 <code>:has()</code> 두 개짜리 규칙(0,3,0)보다 특이도가 낮아 <b>시간 만료 바가 뜨는 순간 밀려났다</b>. 숨긴 축어록·컨트롤바의 빈 트랙이 남아 화면 절반이 비었다(<code>80px 59px 628px 0px 116px</code> 5행). 게이트 규칙에도 같은 <code>:has()</code>를 붙여 <b>3행</b>(<code>80px 59px 693px</code>)으로 정정했다. 무회귀: <code>typecheck</code> 통과, <code>session-layout</code>+<code>alliance-pulse</code>+<code>layout-visual-gate</code> <b>31/31</b>(182차에서 실패했던 <code>settings</code>도 통과), 시간만료 종료 다이얼로그 E2E <b>2/2</b>, pre/mid 잠금 E2E <b>2/2</b>, <code>full-sweep-session</code>+<code>self-directed-learning-loop</code> 30 pass. 남은 음성 4건(<code>voice skip</code>·<code>Space mic</code>)은 변경분 stash 후 baseline에서도 동일 실패라 이 변경과 무관한 기존 회귀다.</p>
|
||||||
<div class="dg-principles" aria-label="디자인 생성 가드레일">
|
<div class="dg-principles" aria-label="디자인 생성 가드레일">
|
||||||
<div><b>래스터만 사용</b><span>이미지 생성 도구 산출물은 PNG 기반 시안이다. SVG·벡터·와이어프레임·로고 시트로 해석하지 않는다.</span></div>
|
<div><b>래스터만 사용</b><span>이미지 생성 도구 산출물은 PNG 기반 시안이다. SVG·벡터·와이어프레임·로고 시트로 해석하지 않는다.</span></div>
|
||||||
<div><b>기능 우선</b><span>메인 라우트의 실제 액션과 정보 구조를 먼저 반영한다. 장식은 기능을 가리지 않는 수준에서만 쓴다.</span></div>
|
<div><b>기능 우선</b><span>메인 라우트의 실제 액션과 정보 구조를 먼저 반영한다. 장식은 기능을 가리지 않는 수준에서만 쓴다.</span></div>
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 324 KiB After Width: | Height: | Size: 364 KiB |
|
Before Width: | Height: | Size: 513 KiB After Width: | Height: | Size: 581 KiB |
|
Before Width: | Height: | Size: 203 KiB After Width: | Height: | Size: 244 KiB |
|
Before Width: | Height: | Size: 521 KiB After Width: | Height: | Size: 551 KiB |
|
Before Width: | Height: | Size: 709 KiB After Width: | Height: | Size: 709 KiB |
|
Before Width: | Height: | Size: 391 KiB After Width: | Height: | Size: 391 KiB |
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 809 KiB After Width: | Height: | Size: 810 KiB |