미디어위키:MainPage.css: 두 판 사이의 차이

(새 문서: →‎========================================= COASTLINE: BLACK ICE - MainPage 대문 전용 스타일 =========================================: /* 설계 목적 ----------------------------------------- 이 파일은 대문 전용 화면 구성만 담당한다. 이전 테스트 단계에서는 MediaWiki:Test.css 안에 대문 레이아웃, 이미지 피드, 방명록 더미, 스타일 매뉴얼, 카테고리 네비 실험 코드가 모두 섞여 있었다. 그 상태로 실...)
 
편집 요약 없음
태그: 수동 되돌리기
 
(같은 사용자의 중간 판 8개는 보이지 않습니다)
2번째 줄: 2번째 줄:
COASTLINE: BLACK ICE - MainPage
COASTLINE: BLACK ICE - MainPage
대문 전용 스타일
대문 전용 스타일
Full portal layout + SVG category navigation
========================================= */
========================================= */


7번째 줄: 8번째 줄:
설계 목적
설계 목적
-----------------------------------------
-----------------------------------------
이 파일은 대문 전용 화면 구성만 담당한다.
이 파일은 대문 전용 화면 전체를 담당한다.


이전 테스트 단계에서는 MediaWiki:Test.css 안에
이전 Test.css는 실험용이었다.
대문 레이아웃, 이미지 피드, 방명록 더미, 스타일 매뉴얼,
그 안에는 대문 레이아웃, 공지, 로고, 이미지 피드, 방명록 더미,
카테고리 네비 실험 코드가 모두 섞여 있었다.
프로젝트 트래커, 상태 패널, 푸터, 카테고리 네비 실험 코드가 섞여 있었다.


그 상태로 실사용에 넣으면 다음 문제가 생긴다.
실사용 단계에서는 Test.css를 import하지 않고,
대문에서 필요한 요소를 이 파일로 옮긴다.


1. 어느 코드가 대문 전용인지, 어느 코드가 공통 컴포넌트인지 구분하기 어렵다.
중요:
2. 나중에 일반 문서 네비바나 사이드바를 고칠 때 대문 코드가 같이 영향을 받을 수 있다.
- 파일의 스코프는 .main-portal이다.
3. 테스트용 더미와 실제 운영 UI가 같은 파일에 남아 유지보수가 어려워진다.
- .stylelab은 테스트용 이름이므로 실사용 본문에서 쓰지 않는다.
 
- 카테고리 네비는 CategoryNav.js가 SVG DOM을 생성하고, 이 파일은 시각 스타일만 담당한다.
따라서 파일은 대문 본문 안에서 쓰는
- 사선 버튼처럼 클릭/호버 영역과 시각 형태가 일치해야 하는 요소는 SVG를 사용한다.
.main-portal 스코프 내부만 담당한다.
 
주의:
- body, .liberty-content, .content-wrapper 같은 전역 레이아웃은 건드리지 않는다.
- 상단 공통 네비바, 좌우 사이드바, 관리자 패널은 이 파일의 책임이 아니다.
- 카테고리 네비는 JS가 DOM을 생성하고, 이 파일은 그 결과물의 모양만 담당한다.
*/
*/


31번째 줄: 27번째 줄:
0. Main portal scope
0. Main portal scope
----------------------------------------- */
----------------------------------------- */
/*
main-portal은 더 이상 대문 전체의 외곽 우물 역할을 하지 않는다.
현재 외곽 프레임과 내부 우물은 Layout.css의 .liberty-content-main / .mw-parser-output이 담당한다.
따라서 이 래퍼는 변수와 스코프만 제공하고, 실제 첫 시각 요소는 notice-rail, lab-logo,
category-nav, console 같은 대문 모듈이 바로 담당한다.
*/


.main-portal {
.main-portal {
39번째 줄: 42번째 줄:
--surface-title-hover:#252525;
--surface-title-hover:#252525;
--surface-well:#080808;
--surface-well:#080808;
--surface-screen:#060606;
--surface-row:#121212;
--surface-row-alt:#151515;
--surface-inset:#070707;


--edge-top:#555555;
--edge-top:#555555;
44번째 줄: 51번째 줄:
--edge-bottom:#050505;
--edge-bottom:#050505;
--edge-soft:#202020;
--edge-soft:#202020;
--edge-mid:#333333;
--edge-weak:#181818;


--text-main:#e2e2e2;
--text-main:#e2e2e2;
49번째 줄: 58번째 줄:
--text-dim:#8a8a8a;
--text-dim:#8a8a8a;
--text-faint:#626262;
--text-faint:#626262;
--wip-yellow:#f2c300;
--wip-black:#050505;


--space-block:8px;
--space-block:8px;
--space-inner:6px;
--space-inner:6px;
--space-tight:4px;
--pad-panel:8px;
--pad-panel:8px;


--title-h:24px;
--screen-title-h:22px;
--nav-h:30px;
--nav-h:30px;
--ticker-h:22px;
--row-h:23px;
--gauge-h:48px;


width:100%;
width:100%;
margin:0 auto;
margin:0;
padding:var(--space-block);
padding:0;
box-sizing:border-box;
box-sizing:border-box;
background:var(--surface-page);
background:transparent;
color:var(--text-main);
color:var(--text-main);
font-size:11px;
font-size:11px;
83번째 줄: 101번째 줄:


/* -----------------------------------------
/* -----------------------------------------
1. Category nav mount
1. Frame system
----------------------------------------- */
----------------------------------------- */


/*
.main-portal .notice-rail,
[data-component="category-nav"]는 본문에 남겨두는 자리 표시자다.
.main-portal .lab-logo,
.main-portal .console,
.main-portal .panel,
.main-portal .instrument-panel,
.main-portal .ticker {
border:1px solid;
border-color:
var(--edge-top)
var(--edge-side)
var(--edge-bottom)
var(--edge-side);
background:var(--surface-frame);
border-radius:0;
box-shadow:none;
outline:none;
overflow:hidden;
}


본문에 위키문법으로 링크를 직접 조립하면,
.main-portal .titlebar {
MediaWiki가 생성한 <a>와 바깥 span이 분리된다.
position:relative;
그 상태에서는 실제 클릭 영역, 호버 영역, 사선 모양이 서로 어긋난다.
height:var(--title-h);
min-height:var(--title-h);
padding:0 var(--pad-panel);
display:flex;
align-items:center;
justify-content:space-between;
gap:var(--space-block);
background:var(--surface-title);
border-bottom:1px solid var(--edge-bottom);
color:#f0f0f0;
font-size:10px;
font-weight:700;
line-height:1;
white-space:nowrap;
overflow:hidden;
text-shadow:1px 1px 0 #000;
}


따라서 본문에는 비어 있는 mount만 두고,
.main-portal .titlebar > span {
CategoryNav.js가 실제 <a class="..."> 요소들을 생성한다.
display:block;
min-width:0;
overflow:hidden;
text-overflow:ellipsis;
}


이렇게 하면 하나의 <a>가 다음 역할을 동시에 담당한다.
.main-portal .titlebar > span:first-child {
flex:1 1 auto;
}


1. 보이는 항목
.main-portal .titlebar > span:last-child {
2. 클릭되는 링크
flex:0 0 auto;
3. 호버되는 면
color:#a8a8a8;
4. clip-path로 잘린 사선 버튼
font-weight:700;
text-align:right;
}


이 구조가 이번 문제의 핵심 해결책이다.
/* -----------------------------------------
*/
2. Notice rail
----------------------------------------- */


.main-portal [data-component="category-nav"] {
.main-portal .notice-rail {
display:block;
width:100%;
width:100%;
margin:0 0 var(--space-block);
margin:0 0 var(--space-block);
background:#101010;
border-color:
#606060
var(--edge-side)
var(--edge-bottom)
var(--edge-side);
}
.main-portal .notice-line {
position:relative;
display:grid;
grid-template-columns:72px 96px minmax(0,1fr) 48px;
align-items:center;
gap:7px;
min-height:30px;
padding:0 8px;
border-bottom:1px solid #050505;
background:#141414;
font-size:12px;
line-height:1.25;
color:#dcdcdc;
overflow:hidden;
}
.main-portal .notice-line:last-child {
border-bottom:none;
}
.main-portal .notice-line > * {
position:relative;
z-index:2;
}
.main-portal .notice-line:hover {
background:#1c1c1c;
color:#ffffff;
}
.main-portal .notice-line.is-alert {
background:
repeating-linear-gradient(
45deg,
var(--wip-yellow) 0 9px,
var(--wip-yellow) 9px 10px,
var(--wip-black) 10px 19px,
var(--wip-black) 19px 20px
);
}
.main-portal .notice-line.is-alert::before {
content:"";
position:absolute;
inset:0;
z-index:1;
pointer-events:none;
background:rgba(0,0,0,0.42);
}
.main-portal .notice-line.is-alert:hover::before {
background:rgba(0,0,0,0.34);
}
.main-portal .notice-line.is-notice {
background:#141414;
}
.main-portal .notice-line.is-notice:hover {
background:#1c1c1c;
}
.main-portal .notice-badge {
display:inline-flex;
align-items:center;
justify-content:center;
height:20px;
padding:0 7px;
border:1px solid #444444;
background:#0b0b0b;
color:#d8d8d8;
font-size:10px;
font-weight:700;
line-height:1;
text-shadow:1px 1px 0 #000;
}
.main-portal .notice-line.is-alert .notice-badge,
.main-portal .notice-line.is-notice .notice-badge {
border-color:#505050;
background:#101010;
color:#eeeeee;
}
.main-portal .notice-date {
color:#9d9d9d;
white-space:nowrap;
font-size:10px;
font-family:Consolas, Monaco, monospace !important;
}
.main-portal .notice-text {
min-width:0;
overflow:hidden;
text-overflow:ellipsis;
white-space:nowrap;
color:#e0e0e0;
font-size:12px;
font-weight:700;
}
.main-portal .notice-line.is-alert .notice-badge,
.main-portal .notice-line.is-alert .notice-date,
.main-portal .notice-line.is-alert .notice-text,
.main-portal .notice-line.is-alert .notice-action {
text-shadow:
-1px 0 0 #000,
1px 0 0 #000,
0 -1px 0 #000,
0 1px 0 #000,
-1px -1px 0 #000,
1px -1px 0 #000,
-1px 1px 0 #000,
1px 1px 0 #000;
}
.main-portal .notice-line:hover .notice-text {
color:#ffffff;
}
.main-portal .notice-action {
justify-self:end;
display:inline-flex;
align-items:center;
justify-content:center;
height:20px;
min-width:38px;
padding:0 5px;
border:1px solid #3a3a3a;
background:#0b0b0b;
color:#c8c8c8;
font-size:10px;
font-weight:700;
white-space:nowrap;
line-height:1;
}
.main-portal .notice-line:hover .notice-action {
border-color:#606060;
background:#181818;
color:#ffffff;
}
}


/* -----------------------------------------
/* -----------------------------------------
2. Category navigation
3. Top logo
----------------------------------------- */
----------------------------------------- */


/*
.main-portal .lab-logo {
카테고리 바의 시각 규칙
width:100%;
-----------------------------------------
height:auto;
- 전체는 하나의 긴 바처럼 보여야 한다.
margin:0 0 var(--space-block);
- 각 항목은 독립 버튼처럼 외장을 가지면 안 된다.
padding:10px 0 8px;
- 부모 .portal-category-nav가 표면, 테두리, 상하 입체감을 담당한다.
text-align:center;
- 각 .portal-cat-link는 클릭 영역과 호버 영역만 담당한다.
line-height:0;
- 왼쪽 3개 항목은 \ 방향으로 잘린다.
background:#0b0b0b;
- 가운데 프로젝트는 \ PROJECT / 형태다.
}
- 오른쪽 3개 항목은 / 방향으로 잘린다.
- 검은 획은 항목 외장이 아니라 구분선이다.


이전 실패 기록
.main-portal .lab-logo a,
-----------------------------------------
.main-portal .lab-logo .mw-file-description {
1. span 안에 [[위키링크]]를 넣고 span에 clip-path를 주는 방식:
display:inline-block;
  MediaWiki가 만든 <a>와 span이 분리되어 클릭/호버/시각 범위가 계속 엇갈렸다.
line-height:0;
}


2. ::after로 사선 획을 각 버튼 내부에 넣는 방식:
.main-portal .lab-logo img,
  clip-path가 ::after까지 잘라먹어서 왼쪽 획이 사라지거나 위치가 어긋났다.
.main-portal .lab-logo .mw-file-element {
width:760px;
max-width:90%;
height:auto;
filter:
grayscale(1)
contrast(1.04)
brightness(1.02);
}


3. 버튼끼리 음수 margin으로 겹치는 방식:
/* -----------------------------------------
  빈 사선 영역은 메워졌지만 호버 배경이 옆 칸으로 넘어갔다.
4. Category navigation mount
----------------------------------------- */


4. 호버와 획을 별도 overlay로 분리하는 방식:
.main-portal [data-component="category-nav"] {
  JS 없이 좌표를 수동 계산해야 해서 프로젝트 폭이 바뀔 때 유지보수가 어려웠다.
display:block;
width:100%;
margin:0 0 var(--space-block);
}
 
/* -----------------------------------------
5. Category navigation - SVG version
----------------------------------------- */


이번 구조
/*
-----------------------------------------
SVG 방식에서는 항목이 polygon이다.
JS가 실제 <a class="portal-cat-link ...">를 생성한다.
보이는 면, hover 면, click 면, 검은 구분선이 같은 SVG 좌표계를 사용한다.
링크 자체에 clip-path와 :hover를 적용한다.
 
따라서 보이는 모양, 클릭 범위, 호버 범위가 같은 요소에 묶인다.
CSS는 색상, 호버, 텍스트 스타일, 외곽 표면만 담당한다.
버튼 폭과 사선 좌표는 CategoryNav.js가 계산한다.
*/
*/


.portal-category-nav {
.portal-category-nav {
--cat-cell-w:163px;
--cat-cut:10px;
position:relative;
position:relative;
display:grid;
grid-template-columns:
var(--cat-cell-w)
var(--cat-cell-w)
var(--cat-cell-w)
minmax(180px,1fr)
var(--cat-cell-w)
var(--cat-cell-w)
var(--cat-cell-w);
width:100%;
width:100%;
height:var(--nav-h);
height:var(--nav-h);
181번째 줄: 392번째 줄:
}
}


/*
.portal-category-svg {
각 링크는 실제 버튼이다.
display:block;
width:100%;
height:100%;
}
 
.portal-cat-shape {
fill:transparent;
stroke:none;
pointer-events:all;
}
 
.portal-cat-anchor:hover .portal-cat-shape {
fill:rgba(255,255,255,0.035);
}
 
.portal-cat-anchor:active .portal-cat-shape {
fill:rgba(0,0,0,0.20);
}
 
.portal-cat-label {
fill:var(--text-main);
font-size:10px;
font-weight:700;
font-family:inherit;
text-anchor:middle;
dominant-baseline:middle;
pointer-events:none;
paint-order:stroke;
stroke:#000000;
stroke-width:2px;
stroke-linejoin:round;
}
 
.portal-cat-anchor:hover .portal-cat-label {
fill:#ffffff;
}
 
.portal-cat-project-label {
font-size:11px;
letter-spacing:0.2px;
}
 
.portal-cat-divider {
stroke:#050505;
stroke-width:1;
shape-rendering:crispEdges;
vector-effect:non-scaling-stroke;
pointer-events:none;
}
 
/* -----------------------------------------
6. Main console
----------------------------------------- */
 
.main-portal .console {
width:100%;
margin:0 0 var(--space-block);
padding:0;
}
 
.main-portal .console-body {
padding:var(--space-inner);
background:var(--surface-frame);
}
 
.main-portal .console-grid {
display:grid;
grid-template-columns:minmax(0,1fr) 260px;
gap:var(--space-inner);
align-items:stretch;
}
 
.main-portal .main-screen,
.main-portal .side-screen {
position:relative;
min-width:0;
height:100%;
overflow:hidden;
border:1px solid var(--edge-soft);
background:var(--surface-screen);
box-shadow:
inset 0 0 0 1px rgba(255,255,255,0.018),
inset 0 0 18px rgba(0,0,0,0.70);
}
 
.main-portal .main-screen::before,
.main-portal .side-screen::before {
content:"";
position:absolute;
inset:0;
z-index:6;
pointer-events:none;
background:
radial-gradient(
ellipse at 50% 42%,
rgba(255,255,255,0.030) 0%,
transparent 42%
),
linear-gradient(
to bottom,
rgba(255,255,255,0.030) 0%,
transparent 24%,
rgba(0,0,0,0.16) 100%
);
}
 
.main-portal .main-screen::after,
.main-portal .side-screen::after {
content:"";
position:absolute;
inset:-40px 0;
z-index:7;
pointer-events:none;
opacity:0.13;
background:
repeating-linear-gradient(
to bottom,
rgba(255,255,255,0.080) 0px,
rgba(255,255,255,0.080) 1px,
transparent 2px,
transparent 5px
);
mix-blend-mode:screen;
}
 
.main-portal .main-screen > *,
.main-portal .side-screen > * {
position:relative;
z-index:3;
}
 
.main-portal .main-screen {
min-height:392px;
padding:var(--pad-panel);
display:flex;
flex-direction:column;
justify-content:flex-start;
}
 
.main-portal .side-screen {
padding:var(--pad-panel);
display:flex;
flex-direction:column;
}


여기서는 개별 버튼 외장을 만들지 않는다.
/* -----------------------------------------
background는 평소 transparent이고, hover 때만 아주 약하게 밝아진다.
7. Image feed
개별 border, 개별 box-shadow를 넣으면 7개의 독립 버튼처럼 보이므로 금지한다.
----------------------------------------- */
*/


.portal-cat-link {
.main-portal .image-feed {
position:relative;
position:relative;
display:flex !important;
width:100%;
align-items:center !important;
height:240px;
justify-content:center !important;
min-height:240px;
height:var(--nav-h);
overflow:hidden;
border:1px solid var(--edge-soft);
background:#030303;
isolation:isolate;
}
 
.main-portal .feed-layer {
position:absolute;
inset:-12px;
z-index:1;
opacity:0;
background-size:cover;
background-position:center center;
background-repeat:no-repeat;
filter:
brightness(0.78)
contrast(1.12)
saturate(0.78)
grayscale(0.16);
transform:translate(0,0) scale(1.06);
transform-origin:center center;
will-change:opacity, transform;
animation-duration:24s;
animation-timing-function:linear;
animation-iteration-count:infinite;
}
 
.main-portal .feed-bg-001 {
background-image:url('/index.php?title=특수:Redirect/file/Bg-Feed-001.png');
}
 
.main-portal .feed-bg-002 {
background-image:url('/index.php?title=특수:Redirect/file/Bg-Feed-002.png');
}
 
.main-portal .feed-bg-003 {
background-image:url('/index.php?title=특수:Redirect/file/Bg-Feed-003.png');
}
 
.main-portal .feed-bg-004 {
background-image:url('/index.php?title=특수:Redirect/file/Bg-Feed-004.png');
}
 
.main-portal .feed-layer-1 {
animation-name:image-feed-drift-left;
animation-delay:0s;
}
 
.main-portal .feed-layer-2 {
animation-name:image-feed-drift-right;
animation-delay:6s;
}
 
.main-portal .feed-layer-3 {
animation-name:image-feed-drift-left;
animation-delay:12s;
}
 
.main-portal .feed-layer-4 {
animation-name:image-feed-drift-right;
animation-delay:18s;
}
 
@keyframes image-feed-drift-left {
0% { opacity:0; transform:translate(0,0) scale(1.06); }
4.166% { opacity:1; transform:translate(0,0) scale(1.06); }
25% { opacity:1; transform:translate(-22px,14px) scale(1.075); }
29.166% { opacity:0; transform:translate(-26px,16px) scale(1.078); }
100% { opacity:0; transform:translate(0,0) scale(1.06); }
}
 
@keyframes image-feed-drift-right {
0% { opacity:0; transform:translate(0,0) scale(1.06); }
4.166% { opacity:1; transform:translate(0,0) scale(1.06); }
25% { opacity:1; transform:translate(22px,14px) scale(1.075); }
29.166% { opacity:0; transform:translate(26px,16px) scale(1.078); }
100% { opacity:0; transform:translate(0,0) scale(1.06); }
}
 
.main-portal .image-feed::before {
content:"";
position:absolute;
inset:0;
z-index:5;
pointer-events:none;
background:
linear-gradient(
to bottom,
rgba(0,0,0,0.18) 0%,
transparent 22%,
transparent 74%,
rgba(0,0,0,0.30) 100%
),
linear-gradient(
to right,
rgba(0,0,0,0.22) 0%,
transparent 10%,
transparent 90%,
rgba(0,0,0,0.22) 100%
);
}
 
.main-portal .image-feed::after {
content:"";
position:absolute;
inset:-40px 0;
z-index:6;
pointer-events:none;
opacity:0.20;
background:
repeating-linear-gradient(
to bottom,
rgba(255,255,255,0.090) 0px,
rgba(255,255,255,0.090) 1px,
transparent 2px,
transparent 5px
);
mix-blend-mode:screen;
}
 
.main-portal .feed-caption {
position:absolute;
left:0;
right:0;
bottom:0;
z-index:8;
height:22px;
padding:0 7px;
display:flex;
align-items:center;
justify-content:space-between;
gap:8px;
border-top:1px solid rgba(255,255,255,0.09);
background:rgba(5,5,5,0.86);
color:#cfcfcf;
font-size:10px;
line-height:22px;
text-shadow:1px 1px 0 #000;
}
 
.main-portal .feed-caption span:first-child {
min-width:0;
min-width:0;
padding:0 10px !important;
overflow:hidden;
box-sizing:border-box !important;
text-overflow:ellipsis;
white-space:nowrap;
}
 
.main-portal .feed-caption span:last-child {
flex:0 0 auto;
color:var(--text-dim);
}
 
/* -----------------------------------------
8. Statement plate
----------------------------------------- */


background:transparent;
.main-portal .statement-plate {
border:none;
margin:var(--space-inner) 0 0;
box-shadow:none;
padding:13px 18px 12px;
border:1px solid var(--edge-soft);
background:#080808;
}


color:var(--text-main) !important;
.main-portal .statement-plate h2 {
font-size:10px !important;
margin:0 0 10px !important;
font-weight:700 !important;
padding:0 0 8px !important;
line-height:1.2 !important;
border-bottom:1px solid var(--edge-soft) !important;
color:#f4f4f4 !important;
font-size:20px !important;
line-height:1.25 !important;
text-align:center !important;
text-align:center !important;
text-decoration:none !important;
text-shadow:
1px 1px 0 #000,
0 0 4px rgba(255,255,255,0.10) !important;
}
 
.main-portal .statement-plate p {
max-width:820px;
margin:0 auto 9px;
color:#d8d8d8;
font-size:12px;
line-height:1.68;
text-align:center;
text-shadow:1px 1px 0 #000;
}
 
.main-portal .statement-plate p:last-child {
margin-bottom:0;
}
 
/* -----------------------------------------
9. Guestbook preview
----------------------------------------- */
 
.main-portal .screen-title {
height:var(--screen-title-h);
min-height:var(--screen-title-h);
margin:0 0 var(--space-inner);
padding:0 7px;
display:flex;
align-items:center;
justify-content:space-between;
gap:6px;
border:1px solid var(--edge-soft);
background:#151515;
color:#f0f0f0;
font-size:10px;
font-weight:700;
line-height:1;
text-shadow:1px 1px 0 #000;
}
 
.main-portal .screen-title span:last-child {
color:var(--text-dim);
font-size:9px;
}
 
.main-portal .guestbook-device {
flex:1 1 auto;
min-height:0;
display:flex;
flex-direction:column;
gap:var(--space-inner);
}
 
.main-portal .guestbook-messages {
flex:1 1 auto;
min-height:0;
border-top:1px solid var(--edge-soft);
border-bottom:1px solid var(--edge-soft);
overflow:hidden;
}
 
.main-portal .guestbook-message {
padding:6px 2px;
border-bottom:1px solid var(--edge-soft);
}
 
.main-portal .guestbook-message:last-child {
border-bottom:none;
}
 
.main-portal .guestbook-message:hover {
background:#111111;
}
 
.main-portal .guestbook-message-head {
display:flex;
align-items:center;
justify-content:space-between;
gap:6px;
margin-bottom:3px;
font-size:9px;
line-height:1.2;
}
 
.main-portal .guestbook-name {
color:#eeeeee;
font-weight:700;
white-space:nowrap;
}
 
.main-portal .guestbook-time {
color:var(--text-dim);
white-space:nowrap;
}
 
.main-portal .guestbook-text {
color:var(--text-soft);
font-size:10px;
line-height:1.45;
word-break:keep-all;
}
 
.main-portal .guestbook-form {
display:grid;
grid-template-columns:minmax(0,1fr) 48px;
gap:var(--space-tight);
}
 
.main-portal .guestbook-input {
height:24px;
padding:0 7px;
display:flex;
align-items:center;
border:1px solid var(--edge-soft);
background:#070707;
color:var(--text-faint);
font-size:10px;
line-height:1;
overflow:hidden;
white-space:nowrap;
text-overflow:ellipsis;
}
 
.main-portal .guestbook-input:hover {
background:#0c0c0c;
color:#8f8f8f;
}
 
.main-portal .guestbook-submit {
height:24px;
display:flex;
align-items:center;
justify-content:center;
border:1px solid var(--edge-soft);
background:#161616;
color:var(--text-soft);
font-size:9px;
font-weight:700;
line-height:1;
text-shadow:1px 1px 0 #000;
}
 
.main-portal .guestbook-submit:hover {
background:#222222;
color:#ffffff;
}
 
/* -----------------------------------------
10. Ticker
----------------------------------------- */
 
.main-portal .ticker {
height:var(--ticker-h);
margin:0 0 var(--space-inner);
padding:0 var(--pad-panel);
color:#bdbdbd;
background:#171717;
font-size:10px;
line-height:var(--ticker-h);
white-space:nowrap;
white-space:nowrap;
overflow:hidden;
overflow:hidden;
text-overflow:ellipsis;
text-overflow:ellipsis;
text-shadow:1px 1px 0 #000;
}
/* -----------------------------------------
11. Module grid
----------------------------------------- */


text-shadow:
.main-portal .module-grid {
1px 1px 0 #000,
display:grid;
0 0 2px rgba(0,0,0,0.85) !important;
grid-template-columns:minmax(0,1fr) minmax(0,1fr) 280px;
gap:var(--space-inner);
align-items:stretch;
}
}


/*
.main-portal .panel,
중앙 프로젝트는 의미상 허브이므로 글자만 살짝 키운다.
.main-portal .instrument-panel {
표면이나 색으로 강조하지 않는다.
min-width:0;
*/
height:100%;
display:flex;
flex-direction:column;
}


.portal-cat-project {
.main-portal .panel-body,
font-size:11px !important;
.main-portal .instrument-body {
letter-spacing:0.2px;
flex:1 1 auto;
min-height:0;
padding:var(--pad-panel);
background:var(--surface-well);
}
}


/*
/* -----------------------------------------
사선 형태
12. Record modules
-----------------------------------------
----------------------------------------- */
왼쪽군:
 
- 오른쪽 경계가 \ 방향으로 깎인다.
.main-portal .record-list {
margin:0 !important;
padding:0 !important;
list-style:none !important;
}


프로젝트:
.main-portal .record-list li {
- 왼쪽 경계는 \ 쪽과 맞물린다.
display:grid;
- 오른쪽 경계는 / 쪽과 맞물린다.
grid-template-columns:minmax(0,1fr) auto;
align-items:center;
gap:var(--space-block);
height:var(--row-h);
min-height:var(--row-h);
margin:0;
padding:0 2px;
border-bottom:1px solid var(--edge-soft);
color:var(--text-soft);
line-height:1.25;
}


오른쪽군:
.main-portal .record-list li:first-child {
- 왼쪽 경계가 / 방향으로 깎인다.
border-top:1px solid var(--edge-soft);
}


첫 항목과 마지막 항목은 바깥 프레임과 맞닿으므로
.main-portal .record-list li:hover {
바깥쪽은 직각으로 유지한다.
background:#151515;
*/
color:#ffffff;
}


.portal-cat-left-start {
.main-portal .record-list li span:first-child {
clip-path:polygon(
min-width:0;
0 0,
overflow:hidden;
calc(100% - var(--cat-cut)) 0,
text-overflow:ellipsis;
100% 100%,
white-space:nowrap;
0 100%
);
}
}


.portal-cat-left-mid,
.main-portal .meta {
.portal-cat-left-end {
color:var(--text-dim);
clip-path:polygon(
white-space:nowrap;
0 0,
font-size:9px;
calc(100% - var(--cat-cut)) 0,
line-height:1;
100% 100%,
var(--cat-cut) 100%
);
}
}


.portal-cat-project {
.main-portal .record-list li:hover .meta {
clip-path:polygon(
color:#c0c0c0;
0 0,
100% 0,
calc(100% - var(--cat-cut)) 100%,
var(--cat-cut) 100%
);
}
}


.portal-cat-right-start,
/* -----------------------------------------
.portal-cat-right-mid {
13. Status tags
clip-path:polygon(
----------------------------------------- */
var(--cat-cut) 0,
 
100% 0,
.main-portal .status-tag {
calc(100% - var(--cat-cut)) 100%,
display:inline-flex;
0 100%
align-items:center;
);
justify-content:center;
min-width:34px;
height:16px;
padding:0 4px;
border:1px solid #303030;
background:#161616;
color:var(--text-soft);
font-size:9px;
text-align:center;
line-height:1;
}
}


.portal-cat-right-end {
.main-portal .status-tag.is-progress {
clip-path:polygon(
border-color:#6d5b2c;
var(--cat-cut) 0,
background:#19170f;
100% 0,
100% 100%,
0 100%
);
}
}


/*
.main-portal .status-tag.is-review {
호버
border-color:#36515d;
-----------------------------------------
background:#10171a;
호버는 각 <a>에 직접 적용한다.
}
이전처럼 별도 overlay를 좌표로 맞추는 방식은 쓰지 않는다.
그래야 화면 크기와 프로젝트 폭이 바뀌어도 호버 범위가 유지된다.
*/


.portal-cat-link:hover {
.main-portal .status-tag.is-wait {
background:rgba(255,255,255,0.035);
border-color:#484848;
color:#ffffff !important;
background:#151515;
text-decoration:none !important;
}
}


.portal-cat-link:active {
.main-portal .status-tag.is-done {
background:rgba(0,0,0,0.20);
border-color:#3d5c38;
padding-top:1px !important;
background:#111811;
}
}


/*
.main-portal .status-tag.is-alert {
검은 구분 획
border-color:#6a3933;
-----------------------------------------
background:#1b1110;
획은 각 링크 안에서 그린다.
}


이전에는 clip-path가 ::after를 잘라먹어서
/* -----------------------------------------
왼쪽 항목의 획이 사라지는 문제가 있었다.
14. Team work module
이번 구조에서는 <a> 자체가 링크이므로 시각과 호버가 일치하고,
----------------------------------------- */
획도 버튼 내부에서 같은 clip-path 기준으로 움직인다.


다만 획이 지나치게 두꺼우면 '틈'처럼 보이므로 1px로 둔다.
.main-portal .team-panel .panel-body {
*/
position:relative;
padding-left:15px;
}


.portal-cat-link::after {
.main-portal .team-panel .panel-body::before {
content:"";
content:"";
position:absolute;
position:absolute;
top:-5px;
left:6px;
bottom:-5px;
top:8px;
right:calc(var(--cat-cut) - 1px);
bottom:8px;
width:1px;
width:4px;
z-index:3;
pointer-events:none;
pointer-events:none;
background:#050505;
background:
opacity:0.96;
repeating-linear-gradient(
display:none;
45deg,
transform-origin:top center;
var(--wip-yellow) 0 4px,
var(--wip-yellow) 4px 5px,
var(--wip-black) 5px 9px,
var(--wip-black) 9px 10px
);
opacity:0.95;
}
 
.main-portal .team-panel .titlebar > span:last-child {
color:#c6c6c6;
}
 
/* -----------------------------------------
15. Status instrument
----------------------------------------- */
 
.main-portal .instrument-grid {
display:grid;
grid-template-columns:minmax(0,1fr) minmax(0,1fr);
gap:var(--space-inner);
margin:0 0 var(--space-inner);
}
 
.main-portal .gauge {
height:var(--gauge-h);
min-width:0;
padding:6px;
display:flex;
flex-direction:column;
align-items:center;
justify-content:center;
border:1px solid var(--edge-soft);
background:var(--surface-inset);
text-align:center;
box-shadow:
inset 0 1px 0 rgba(255,255,255,0.025),
inset 0 -1px 0 rgba(0,0,0,0.55);
}
 
.main-portal .gauge:hover {
background:#0d0d0d;
}
 
.main-portal .gauge b {
display:block;
color:#f0f0f0;
font-family:Consolas, Monaco, monospace !important;
font-size:18px;
line-height:1;
text-shadow:1px 1px 0 #000;
}
 
.main-portal .gauge span {
display:block;
margin-top:5px;
color:var(--text-dim);
font-size:9px;
line-height:1;
}
 
.main-portal .instrument-lines {
border-top:1px solid var(--edge-soft);
border-bottom:1px solid var(--edge-soft);
}
 
.main-portal .instrument-line {
display:grid;
grid-template-columns:82px minmax(0,1fr);
align-items:center;
gap:6px;
height:22px;
min-height:22px;
padding:0 2px;
border-bottom:1px solid var(--edge-soft);
font-size:10px;
line-height:1.2;
}
 
.main-portal .instrument-line:last-child {
border-bottom:none;
}
 
.main-portal .instrument-line:hover {
background:#151515;
}
 
.main-portal .instrument-label {
color:var(--text-dim);
white-space:nowrap;
}
 
.main-portal .instrument-value {
color:var(--text-soft);
white-space:nowrap;
overflow:hidden;
text-overflow:ellipsis;
}
 
.main-portal .instrument-line:hover .instrument-value {
color:#ffffff;
}
 
/* -----------------------------------------
16. Footer split
----------------------------------------- */
 
.main-portal .footer-grid {
display:grid;
grid-template-columns:minmax(0,1fr) 300px;
gap:var(--space-inner);
margin-top:var(--space-inner);
align-items:stretch;
}
 
.main-portal .footer-grid .panel {
height:100%;
}
 
.main-portal .manual-panel {
margin-top:0;
}
 
.main-portal .sample-row {
display:grid;
grid-template-columns:150px minmax(0,1fr);
align-items:center;
gap:var(--space-inner);
min-height:24px;
padding:4px 0;
border-bottom:1px solid var(--edge-soft);
}
 
.main-portal .sample-row:first-child {
border-top:1px solid var(--edge-soft);
}
 
.main-portal .sample-row:hover {
background:#151515;
}
 
.main-portal .sample-label {
color:var(--text-dim);
font-size:10px;
line-height:1.25;
}
 
.main-portal .sample-value {
color:var(--text-soft);
font-size:10px;
line-height:1.35;
}
 
.main-portal .sample-row:hover .sample-value {
color:#ffffff;
}
 
.main-portal .policy-list {
border-top:1px solid var(--edge-soft);
border-bottom:1px solid var(--edge-soft);
}
 
.main-portal .policy-row {
display:grid;
grid-template-columns:minmax(0,1fr) 18px;
align-items:center;
gap:var(--space-inner);
height:24px;
padding:0 2px;
border-bottom:1px solid var(--edge-soft);
font-size:10px;
line-height:1.2;
}
 
.main-portal .policy-row:last-child {
border-bottom:none;
}
 
.main-portal .policy-row:hover {
background:#151515;
}
}


.portal-cat-left-start::after,
.main-portal .policy-row a {
.portal-cat-left-mid::after,
.portal-cat-left-end::after {
display:block;
display:block;
transform:skewX(18.5deg);
min-width:0;
overflow:hidden;
text-overflow:ellipsis;
white-space:nowrap;
color:var(--text-soft) !important;
}
 
.main-portal .policy-row:hover a {
color:#ffffff !important;
text-decoration:none !important;
}
 
.main-portal .policy-mark {
color:var(--text-dim);
font-size:14px;
line-height:1;
white-space:nowrap;
text-align:right;
}
 
.main-portal .policy-row:hover .policy-mark {
color:#ffffff;
}
 
.main-portal .social-strip {
display:grid;
grid-template-columns:repeat(4,1fr);
gap:var(--space-tight);
margin-top:var(--space-inner);
}
}


.portal-cat-project::after,
.main-portal .social-icon {
.portal-cat-right-start::after,
.portal-cat-right-mid::after {
display:block;
display:block;
right:0;
height:26px;
transform:skewX(-18.5deg);
min-width:0;
}
 
.main-portal .social-icon a {
display:flex !important;
align-items:center !important;
justify-content:center !important;
width:100% !important;
height:26px !important;
border:1px solid var(--edge-soft) !important;
background:#111111 !important;
color:var(--text-soft) !important;
font-size:10px !important;
font-weight:700 !important;
line-height:1 !important;
text-decoration:none !important;
text-shadow:1px 1px 0 #000 !important;
box-sizing:border-box !important;
}
 
.main-portal .social-icon a:hover {
background:#1c1c1c !important;
color:#ffffff !important;
border-color:#3a3a3a !important;
text-decoration:none !important;
}
 
/* -----------------------------------------
17. Responsive
----------------------------------------- */
 
@media screen and (max-width:1380px) {
.main-portal .module-grid {
grid-template-columns:minmax(0,1fr) minmax(0,1fr);
}
}


/*
.main-portal .footer-grid {
반응형
grid-template-columns:minmax(0,1fr);
-----------------------------------------
}
좁은 화면에서는 사선 구조를 유지하면 읽기가 어려워진다.
}
대문용 큰 네비의 축소판을 억지로 유지하지 않고,
한 줄씩 쌓이는 일반 버튼 목록으로 전환한다.
*/


@media screen and (max-width:1180px) {
@media screen and (max-width:1180px) {
.portal-category-nav {
.main-portal .console-grid {
height:auto;
grid-template-columns:minmax(0,1fr);
}
 
.main-portal .module-grid {
grid-template-columns:minmax(0,1fr);
grid-template-columns:minmax(0,1fr);
}
}


.portal-cat-link,
.main-portal .notice-line {
.portal-cat-project {
grid-template-columns:62px minmax(0,1fr);
height:var(--nav-h);
min-height:32px;
clip-path:none;
}
}


.portal-cat-link::after {
.main-portal .notice-date,
display:none !important;
.main-portal .notice-action {
display:none;
}
}
}
/* -----------------------------------------
Analog hover normalization
----------------------------------------- */
.main-portal a,
.main-portal button,
.main-portal .notice-line,
.main-portal .notice-line *,
.main-portal .notice-action,
.main-portal .portal-cat-anchor .portal-cat-shape,
.main-portal .portal-cat-anchor .portal-cat-label,
.main-portal .record-link,
.main-portal .record-link *,
.main-portal .archive-row,
.main-portal .archive-row *,
.main-portal .tracker-row,
.main-portal .tracker-row *,
.main-portal .guestbook-row,
.main-portal .guestbook-row * {
transition:none !important;
}
}

2026년 6월 2일 (화) 02:28 기준 최신판

/* =========================================
COASTLINE: BLACK ICE - MainPage
대문 전용 스타일
Full portal layout + SVG category navigation
========================================= */

/*
설계 목적
-----------------------------------------
이 파일은 대문 전용 화면 전체를 담당한다.

이전 Test.css는 실험용이었다.
그 안에는 대문 레이아웃, 공지, 로고, 이미지 피드, 방명록 더미,
프로젝트 트래커, 상태 패널, 푸터, 카테고리 네비 실험 코드가 섞여 있었다.

실사용 단계에서는 Test.css를 import하지 않고,
대문에서 필요한 요소를 이 파일로 옮긴다.

중요:
- 이 파일의 스코프는 .main-portal이다.
- .stylelab은 테스트용 이름이므로 실사용 본문에서 쓰지 않는다.
- 카테고리 네비는 CategoryNav.js가 SVG DOM을 생성하고, 이 파일은 시각 스타일만 담당한다.
- 사선 버튼처럼 클릭/호버 영역과 시각 형태가 일치해야 하는 요소는 SVG를 사용한다.
*/

/* -----------------------------------------
0. Main portal scope
----------------------------------------- */

/*
main-portal은 더 이상 대문 전체의 외곽 우물 역할을 하지 않는다.
현재 외곽 프레임과 내부 우물은 Layout.css의 .liberty-content-main / .mw-parser-output이 담당한다.
따라서 이 래퍼는 변수와 스코프만 제공하고, 실제 첫 시각 요소는 notice-rail, lab-logo,
category-nav, console 같은 대문 모듈이 바로 담당한다.
*/

.main-portal {
--surface-page:#070707;
--surface-frame:#101010;
--surface-frame-soft:#141414;
--surface-title:#1d1d1d;
--surface-title-hover:#252525;
--surface-well:#080808;
--surface-screen:#060606;
--surface-row:#121212;
--surface-row-alt:#151515;
--surface-inset:#070707;

--edge-top:#555555;
--edge-side:#2b2b2b;
--edge-bottom:#050505;
--edge-soft:#202020;
--edge-mid:#333333;
--edge-weak:#181818;

--text-main:#e2e2e2;
--text-soft:#c8c8c8;
--text-dim:#8a8a8a;
--text-faint:#626262;

--wip-yellow:#f2c300;
--wip-black:#050505;

--space-block:8px;
--space-inner:6px;
--space-tight:4px;
--pad-panel:8px;

--title-h:24px;
--screen-title-h:22px;
--nav-h:30px;
--ticker-h:22px;
--row-h:23px;
--gauge-h:48px;

width:100%;
margin:0;
padding:0;
box-sizing:border-box;
background:transparent;
color:var(--text-main);
font-size:11px;
line-height:1.5;
}

.main-portal *,
.main-portal *::before,
.main-portal *::after {
box-sizing:border-box;
}

.main-portal a {
color:var(--text-main) !important;
text-decoration:none !important;
}

.main-portal a:hover {
color:#ffffff !important;
text-decoration:none !important;
}

/* -----------------------------------------
1. Frame system
----------------------------------------- */

.main-portal .notice-rail,
.main-portal .lab-logo,
.main-portal .console,
.main-portal .panel,
.main-portal .instrument-panel,
.main-portal .ticker {
border:1px solid;
border-color:
var(--edge-top)
var(--edge-side)
var(--edge-bottom)
var(--edge-side);
background:var(--surface-frame);
border-radius:0;
box-shadow:none;
outline:none;
overflow:hidden;
}

.main-portal .titlebar {
position:relative;
height:var(--title-h);
min-height:var(--title-h);
padding:0 var(--pad-panel);
display:flex;
align-items:center;
justify-content:space-between;
gap:var(--space-block);
background:var(--surface-title);
border-bottom:1px solid var(--edge-bottom);
color:#f0f0f0;
font-size:10px;
font-weight:700;
line-height:1;
white-space:nowrap;
overflow:hidden;
text-shadow:1px 1px 0 #000;
}

.main-portal .titlebar > span {
display:block;
min-width:0;
overflow:hidden;
text-overflow:ellipsis;
}

.main-portal .titlebar > span:first-child {
flex:1 1 auto;
}

.main-portal .titlebar > span:last-child {
flex:0 0 auto;
color:#a8a8a8;
font-weight:700;
text-align:right;
}

/* -----------------------------------------
2. Notice rail
----------------------------------------- */

.main-portal .notice-rail {
width:100%;
margin:0 0 var(--space-block);
background:#101010;
border-color:
#606060
var(--edge-side)
var(--edge-bottom)
var(--edge-side);
}

.main-portal .notice-line {
position:relative;
display:grid;
grid-template-columns:72px 96px minmax(0,1fr) 48px;
align-items:center;
gap:7px;
min-height:30px;
padding:0 8px;
border-bottom:1px solid #050505;
background:#141414;
font-size:12px;
line-height:1.25;
color:#dcdcdc;
overflow:hidden;
}

.main-portal .notice-line:last-child {
border-bottom:none;
}

.main-portal .notice-line > * {
position:relative;
z-index:2;
}

.main-portal .notice-line:hover {
background:#1c1c1c;
color:#ffffff;
}

.main-portal .notice-line.is-alert {
background:
repeating-linear-gradient(
45deg,
var(--wip-yellow) 0 9px,
var(--wip-yellow) 9px 10px,
var(--wip-black) 10px 19px,
var(--wip-black) 19px 20px
);
}

.main-portal .notice-line.is-alert::before {
content:"";
position:absolute;
inset:0;
z-index:1;
pointer-events:none;
background:rgba(0,0,0,0.42);
}

.main-portal .notice-line.is-alert:hover::before {
background:rgba(0,0,0,0.34);
}

.main-portal .notice-line.is-notice {
background:#141414;
}

.main-portal .notice-line.is-notice:hover {
background:#1c1c1c;
}

.main-portal .notice-badge {
display:inline-flex;
align-items:center;
justify-content:center;
height:20px;
padding:0 7px;
border:1px solid #444444;
background:#0b0b0b;
color:#d8d8d8;
font-size:10px;
font-weight:700;
line-height:1;
text-shadow:1px 1px 0 #000;
}

.main-portal .notice-line.is-alert .notice-badge,
.main-portal .notice-line.is-notice .notice-badge {
border-color:#505050;
background:#101010;
color:#eeeeee;
}

.main-portal .notice-date {
color:#9d9d9d;
white-space:nowrap;
font-size:10px;
font-family:Consolas, Monaco, monospace !important;
}

.main-portal .notice-text {
min-width:0;
overflow:hidden;
text-overflow:ellipsis;
white-space:nowrap;
color:#e0e0e0;
font-size:12px;
font-weight:700;
}

.main-portal .notice-line.is-alert .notice-badge,
.main-portal .notice-line.is-alert .notice-date,
.main-portal .notice-line.is-alert .notice-text,
.main-portal .notice-line.is-alert .notice-action {
text-shadow:
-1px 0 0 #000,
1px 0 0 #000,
0 -1px 0 #000,
0 1px 0 #000,
-1px -1px 0 #000,
1px -1px 0 #000,
-1px 1px 0 #000,
1px 1px 0 #000;
}

.main-portal .notice-line:hover .notice-text {
color:#ffffff;
}

.main-portal .notice-action {
justify-self:end;
display:inline-flex;
align-items:center;
justify-content:center;
height:20px;
min-width:38px;
padding:0 5px;
border:1px solid #3a3a3a;
background:#0b0b0b;
color:#c8c8c8;
font-size:10px;
font-weight:700;
white-space:nowrap;
line-height:1;
}

.main-portal .notice-line:hover .notice-action {
border-color:#606060;
background:#181818;
color:#ffffff;
}

/* -----------------------------------------
3. Top logo
----------------------------------------- */

.main-portal .lab-logo {
width:100%;
height:auto;
margin:0 0 var(--space-block);
padding:10px 0 8px;
text-align:center;
line-height:0;
background:#0b0b0b;
}

.main-portal .lab-logo a,
.main-portal .lab-logo .mw-file-description {
display:inline-block;
line-height:0;
}

.main-portal .lab-logo img,
.main-portal .lab-logo .mw-file-element {
width:760px;
max-width:90%;
height:auto;
filter:
grayscale(1)
contrast(1.04)
brightness(1.02);
}

/* -----------------------------------------
4. Category navigation mount
----------------------------------------- */

.main-portal [data-component="category-nav"] {
display:block;
width:100%;
margin:0 0 var(--space-block);
}

/* -----------------------------------------
5. Category navigation - SVG version
----------------------------------------- */

/*
SVG 방식에서는 각 항목이 polygon이다.
보이는 면, hover 면, click 면, 검은 구분선이 같은 SVG 좌표계를 사용한다.

CSS는 색상, 호버, 텍스트 스타일, 외곽 표면만 담당한다.
버튼 폭과 사선 좌표는 CategoryNav.js가 계산한다.
*/

.portal-category-nav {
position:relative;
width:100%;
height:var(--nav-h);
margin:0;
padding:0;
overflow:hidden;

background:var(--surface-frame-soft);
border:1px solid;
border-color:
var(--edge-top)
var(--edge-side)
var(--edge-bottom)
var(--edge-side);
box-shadow:
inset 0 1px 0 rgba(255,255,255,0.055),
inset 0 -1px 0 rgba(0,0,0,0.72);
}

.portal-category-svg {
display:block;
width:100%;
height:100%;
}

.portal-cat-shape {
fill:transparent;
stroke:none;
pointer-events:all;
}

.portal-cat-anchor:hover .portal-cat-shape {
fill:rgba(255,255,255,0.035);
}

.portal-cat-anchor:active .portal-cat-shape {
fill:rgba(0,0,0,0.20);
}

.portal-cat-label {
fill:var(--text-main);
font-size:10px;
font-weight:700;
font-family:inherit;
text-anchor:middle;
dominant-baseline:middle;
pointer-events:none;
paint-order:stroke;
stroke:#000000;
stroke-width:2px;
stroke-linejoin:round;
}

.portal-cat-anchor:hover .portal-cat-label {
fill:#ffffff;
}

.portal-cat-project-label {
font-size:11px;
letter-spacing:0.2px;
}

.portal-cat-divider {
stroke:#050505;
stroke-width:1;
shape-rendering:crispEdges;
vector-effect:non-scaling-stroke;
pointer-events:none;
}

/* -----------------------------------------
6. Main console
----------------------------------------- */

.main-portal .console {
width:100%;
margin:0 0 var(--space-block);
padding:0;
}

.main-portal .console-body {
padding:var(--space-inner);
background:var(--surface-frame);
}

.main-portal .console-grid {
display:grid;
grid-template-columns:minmax(0,1fr) 260px;
gap:var(--space-inner);
align-items:stretch;
}

.main-portal .main-screen,
.main-portal .side-screen {
position:relative;
min-width:0;
height:100%;
overflow:hidden;
border:1px solid var(--edge-soft);
background:var(--surface-screen);
box-shadow:
inset 0 0 0 1px rgba(255,255,255,0.018),
inset 0 0 18px rgba(0,0,0,0.70);
}

.main-portal .main-screen::before,
.main-portal .side-screen::before {
content:"";
position:absolute;
inset:0;
z-index:6;
pointer-events:none;
background:
radial-gradient(
ellipse at 50% 42%,
rgba(255,255,255,0.030) 0%,
transparent 42%
),
linear-gradient(
to bottom,
rgba(255,255,255,0.030) 0%,
transparent 24%,
rgba(0,0,0,0.16) 100%
);
}

.main-portal .main-screen::after,
.main-portal .side-screen::after {
content:"";
position:absolute;
inset:-40px 0;
z-index:7;
pointer-events:none;
opacity:0.13;
background:
repeating-linear-gradient(
to bottom,
rgba(255,255,255,0.080) 0px,
rgba(255,255,255,0.080) 1px,
transparent 2px,
transparent 5px
);
mix-blend-mode:screen;
}

.main-portal .main-screen > *,
.main-portal .side-screen > * {
position:relative;
z-index:3;
}

.main-portal .main-screen {
min-height:392px;
padding:var(--pad-panel);
display:flex;
flex-direction:column;
justify-content:flex-start;
}

.main-portal .side-screen {
padding:var(--pad-panel);
display:flex;
flex-direction:column;
}

/* -----------------------------------------
7. Image feed
----------------------------------------- */

.main-portal .image-feed {
position:relative;
width:100%;
height:240px;
min-height:240px;
overflow:hidden;
border:1px solid var(--edge-soft);
background:#030303;
isolation:isolate;
}

.main-portal .feed-layer {
position:absolute;
inset:-12px;
z-index:1;
opacity:0;
background-size:cover;
background-position:center center;
background-repeat:no-repeat;
filter:
brightness(0.78)
contrast(1.12)
saturate(0.78)
grayscale(0.16);
transform:translate(0,0) scale(1.06);
transform-origin:center center;
will-change:opacity, transform;
animation-duration:24s;
animation-timing-function:linear;
animation-iteration-count:infinite;
}

.main-portal .feed-bg-001 {
background-image:url('/index.php?title=특수:Redirect/file/Bg-Feed-001.png');
}

.main-portal .feed-bg-002 {
background-image:url('/index.php?title=특수:Redirect/file/Bg-Feed-002.png');
}

.main-portal .feed-bg-003 {
background-image:url('/index.php?title=특수:Redirect/file/Bg-Feed-003.png');
}

.main-portal .feed-bg-004 {
background-image:url('/index.php?title=특수:Redirect/file/Bg-Feed-004.png');
}

.main-portal .feed-layer-1 {
animation-name:image-feed-drift-left;
animation-delay:0s;
}

.main-portal .feed-layer-2 {
animation-name:image-feed-drift-right;
animation-delay:6s;
}

.main-portal .feed-layer-3 {
animation-name:image-feed-drift-left;
animation-delay:12s;
}

.main-portal .feed-layer-4 {
animation-name:image-feed-drift-right;
animation-delay:18s;
}

@keyframes image-feed-drift-left {
0% { opacity:0; transform:translate(0,0) scale(1.06); }
4.166% { opacity:1; transform:translate(0,0) scale(1.06); }
25% { opacity:1; transform:translate(-22px,14px) scale(1.075); }
29.166% { opacity:0; transform:translate(-26px,16px) scale(1.078); }
100% { opacity:0; transform:translate(0,0) scale(1.06); }
}

@keyframes image-feed-drift-right {
0% { opacity:0; transform:translate(0,0) scale(1.06); }
4.166% { opacity:1; transform:translate(0,0) scale(1.06); }
25% { opacity:1; transform:translate(22px,14px) scale(1.075); }
29.166% { opacity:0; transform:translate(26px,16px) scale(1.078); }
100% { opacity:0; transform:translate(0,0) scale(1.06); }
}

.main-portal .image-feed::before {
content:"";
position:absolute;
inset:0;
z-index:5;
pointer-events:none;
background:
linear-gradient(
to bottom,
rgba(0,0,0,0.18) 0%,
transparent 22%,
transparent 74%,
rgba(0,0,0,0.30) 100%
),
linear-gradient(
to right,
rgba(0,0,0,0.22) 0%,
transparent 10%,
transparent 90%,
rgba(0,0,0,0.22) 100%
);
}

.main-portal .image-feed::after {
content:"";
position:absolute;
inset:-40px 0;
z-index:6;
pointer-events:none;
opacity:0.20;
background:
repeating-linear-gradient(
to bottom,
rgba(255,255,255,0.090) 0px,
rgba(255,255,255,0.090) 1px,
transparent 2px,
transparent 5px
);
mix-blend-mode:screen;
}

.main-portal .feed-caption {
position:absolute;
left:0;
right:0;
bottom:0;
z-index:8;
height:22px;
padding:0 7px;
display:flex;
align-items:center;
justify-content:space-between;
gap:8px;
border-top:1px solid rgba(255,255,255,0.09);
background:rgba(5,5,5,0.86);
color:#cfcfcf;
font-size:10px;
line-height:22px;
text-shadow:1px 1px 0 #000;
}

.main-portal .feed-caption span:first-child {
min-width:0;
overflow:hidden;
text-overflow:ellipsis;
white-space:nowrap;
}

.main-portal .feed-caption span:last-child {
flex:0 0 auto;
color:var(--text-dim);
}

/* -----------------------------------------
8. Statement plate
----------------------------------------- */

.main-portal .statement-plate {
margin:var(--space-inner) 0 0;
padding:13px 18px 12px;
border:1px solid var(--edge-soft);
background:#080808;
}

.main-portal .statement-plate h2 {
margin:0 0 10px !important;
padding:0 0 8px !important;
border-bottom:1px solid var(--edge-soft) !important;
color:#f4f4f4 !important;
font-size:20px !important;
line-height:1.25 !important;
text-align:center !important;
text-shadow:
1px 1px 0 #000,
0 0 4px rgba(255,255,255,0.10) !important;
}

.main-portal .statement-plate p {
max-width:820px;
margin:0 auto 9px;
color:#d8d8d8;
font-size:12px;
line-height:1.68;
text-align:center;
text-shadow:1px 1px 0 #000;
}

.main-portal .statement-plate p:last-child {
margin-bottom:0;
}

/* -----------------------------------------
9. Guestbook preview
----------------------------------------- */

.main-portal .screen-title {
height:var(--screen-title-h);
min-height:var(--screen-title-h);
margin:0 0 var(--space-inner);
padding:0 7px;
display:flex;
align-items:center;
justify-content:space-between;
gap:6px;
border:1px solid var(--edge-soft);
background:#151515;
color:#f0f0f0;
font-size:10px;
font-weight:700;
line-height:1;
text-shadow:1px 1px 0 #000;
}

.main-portal .screen-title span:last-child {
color:var(--text-dim);
font-size:9px;
}

.main-portal .guestbook-device {
flex:1 1 auto;
min-height:0;
display:flex;
flex-direction:column;
gap:var(--space-inner);
}

.main-portal .guestbook-messages {
flex:1 1 auto;
min-height:0;
border-top:1px solid var(--edge-soft);
border-bottom:1px solid var(--edge-soft);
overflow:hidden;
}

.main-portal .guestbook-message {
padding:6px 2px;
border-bottom:1px solid var(--edge-soft);
}

.main-portal .guestbook-message:last-child {
border-bottom:none;
}

.main-portal .guestbook-message:hover {
background:#111111;
}

.main-portal .guestbook-message-head {
display:flex;
align-items:center;
justify-content:space-between;
gap:6px;
margin-bottom:3px;
font-size:9px;
line-height:1.2;
}

.main-portal .guestbook-name {
color:#eeeeee;
font-weight:700;
white-space:nowrap;
}

.main-portal .guestbook-time {
color:var(--text-dim);
white-space:nowrap;
}

.main-portal .guestbook-text {
color:var(--text-soft);
font-size:10px;
line-height:1.45;
word-break:keep-all;
}

.main-portal .guestbook-form {
display:grid;
grid-template-columns:minmax(0,1fr) 48px;
gap:var(--space-tight);
}

.main-portal .guestbook-input {
height:24px;
padding:0 7px;
display:flex;
align-items:center;
border:1px solid var(--edge-soft);
background:#070707;
color:var(--text-faint);
font-size:10px;
line-height:1;
overflow:hidden;
white-space:nowrap;
text-overflow:ellipsis;
}

.main-portal .guestbook-input:hover {
background:#0c0c0c;
color:#8f8f8f;
}

.main-portal .guestbook-submit {
height:24px;
display:flex;
align-items:center;
justify-content:center;
border:1px solid var(--edge-soft);
background:#161616;
color:var(--text-soft);
font-size:9px;
font-weight:700;
line-height:1;
text-shadow:1px 1px 0 #000;
}

.main-portal .guestbook-submit:hover {
background:#222222;
color:#ffffff;
}

/* -----------------------------------------
10. Ticker
----------------------------------------- */

.main-portal .ticker {
height:var(--ticker-h);
margin:0 0 var(--space-inner);
padding:0 var(--pad-panel);
color:#bdbdbd;
background:#171717;
font-size:10px;
line-height:var(--ticker-h);
white-space:nowrap;
overflow:hidden;
text-overflow:ellipsis;
text-shadow:1px 1px 0 #000;
}

/* -----------------------------------------
11. Module grid
----------------------------------------- */

.main-portal .module-grid {
display:grid;
grid-template-columns:minmax(0,1fr) minmax(0,1fr) 280px;
gap:var(--space-inner);
align-items:stretch;
}

.main-portal .panel,
.main-portal .instrument-panel {
min-width:0;
height:100%;
display:flex;
flex-direction:column;
}

.main-portal .panel-body,
.main-portal .instrument-body {
flex:1 1 auto;
min-height:0;
padding:var(--pad-panel);
background:var(--surface-well);
}

/* -----------------------------------------
12. Record modules
----------------------------------------- */

.main-portal .record-list {
margin:0 !important;
padding:0 !important;
list-style:none !important;
}

.main-portal .record-list li {
display:grid;
grid-template-columns:minmax(0,1fr) auto;
align-items:center;
gap:var(--space-block);
height:var(--row-h);
min-height:var(--row-h);
margin:0;
padding:0 2px;
border-bottom:1px solid var(--edge-soft);
color:var(--text-soft);
line-height:1.25;
}

.main-portal .record-list li:first-child {
border-top:1px solid var(--edge-soft);
}

.main-portal .record-list li:hover {
background:#151515;
color:#ffffff;
}

.main-portal .record-list li span:first-child {
min-width:0;
overflow:hidden;
text-overflow:ellipsis;
white-space:nowrap;
}

.main-portal .meta {
color:var(--text-dim);
white-space:nowrap;
font-size:9px;
line-height:1;
}

.main-portal .record-list li:hover .meta {
color:#c0c0c0;
}

/* -----------------------------------------
13. Status tags
----------------------------------------- */

.main-portal .status-tag {
display:inline-flex;
align-items:center;
justify-content:center;
min-width:34px;
height:16px;
padding:0 4px;
border:1px solid #303030;
background:#161616;
color:var(--text-soft);
font-size:9px;
text-align:center;
line-height:1;
}

.main-portal .status-tag.is-progress {
border-color:#6d5b2c;
background:#19170f;
}

.main-portal .status-tag.is-review {
border-color:#36515d;
background:#10171a;
}

.main-portal .status-tag.is-wait {
border-color:#484848;
background:#151515;
}

.main-portal .status-tag.is-done {
border-color:#3d5c38;
background:#111811;
}

.main-portal .status-tag.is-alert {
border-color:#6a3933;
background:#1b1110;
}

/* -----------------------------------------
14. Team work module
----------------------------------------- */

.main-portal .team-panel .panel-body {
position:relative;
padding-left:15px;
}

.main-portal .team-panel .panel-body::before {
content:"";
position:absolute;
left:6px;
top:8px;
bottom:8px;
width:4px;
pointer-events:none;
background:
repeating-linear-gradient(
45deg,
var(--wip-yellow) 0 4px,
var(--wip-yellow) 4px 5px,
var(--wip-black) 5px 9px,
var(--wip-black) 9px 10px
);
opacity:0.95;
}

.main-portal .team-panel .titlebar > span:last-child {
color:#c6c6c6;
}

/* -----------------------------------------
15. Status instrument
----------------------------------------- */

.main-portal .instrument-grid {
display:grid;
grid-template-columns:minmax(0,1fr) minmax(0,1fr);
gap:var(--space-inner);
margin:0 0 var(--space-inner);
}

.main-portal .gauge {
height:var(--gauge-h);
min-width:0;
padding:6px;
display:flex;
flex-direction:column;
align-items:center;
justify-content:center;
border:1px solid var(--edge-soft);
background:var(--surface-inset);
text-align:center;
box-shadow:
inset 0 1px 0 rgba(255,255,255,0.025),
inset 0 -1px 0 rgba(0,0,0,0.55);
}

.main-portal .gauge:hover {
background:#0d0d0d;
}

.main-portal .gauge b {
display:block;
color:#f0f0f0;
font-family:Consolas, Monaco, monospace !important;
font-size:18px;
line-height:1;
text-shadow:1px 1px 0 #000;
}

.main-portal .gauge span {
display:block;
margin-top:5px;
color:var(--text-dim);
font-size:9px;
line-height:1;
}

.main-portal .instrument-lines {
border-top:1px solid var(--edge-soft);
border-bottom:1px solid var(--edge-soft);
}

.main-portal .instrument-line {
display:grid;
grid-template-columns:82px minmax(0,1fr);
align-items:center;
gap:6px;
height:22px;
min-height:22px;
padding:0 2px;
border-bottom:1px solid var(--edge-soft);
font-size:10px;
line-height:1.2;
}

.main-portal .instrument-line:last-child {
border-bottom:none;
}

.main-portal .instrument-line:hover {
background:#151515;
}

.main-portal .instrument-label {
color:var(--text-dim);
white-space:nowrap;
}

.main-portal .instrument-value {
color:var(--text-soft);
white-space:nowrap;
overflow:hidden;
text-overflow:ellipsis;
}

.main-portal .instrument-line:hover .instrument-value {
color:#ffffff;
}

/* -----------------------------------------
16. Footer split
----------------------------------------- */

.main-portal .footer-grid {
display:grid;
grid-template-columns:minmax(0,1fr) 300px;
gap:var(--space-inner);
margin-top:var(--space-inner);
align-items:stretch;
}

.main-portal .footer-grid .panel {
height:100%;
}

.main-portal .manual-panel {
margin-top:0;
}

.main-portal .sample-row {
display:grid;
grid-template-columns:150px minmax(0,1fr);
align-items:center;
gap:var(--space-inner);
min-height:24px;
padding:4px 0;
border-bottom:1px solid var(--edge-soft);
}

.main-portal .sample-row:first-child {
border-top:1px solid var(--edge-soft);
}

.main-portal .sample-row:hover {
background:#151515;
}

.main-portal .sample-label {
color:var(--text-dim);
font-size:10px;
line-height:1.25;
}

.main-portal .sample-value {
color:var(--text-soft);
font-size:10px;
line-height:1.35;
}

.main-portal .sample-row:hover .sample-value {
color:#ffffff;
}

.main-portal .policy-list {
border-top:1px solid var(--edge-soft);
border-bottom:1px solid var(--edge-soft);
}

.main-portal .policy-row {
display:grid;
grid-template-columns:minmax(0,1fr) 18px;
align-items:center;
gap:var(--space-inner);
height:24px;
padding:0 2px;
border-bottom:1px solid var(--edge-soft);
font-size:10px;
line-height:1.2;
}

.main-portal .policy-row:last-child {
border-bottom:none;
}

.main-portal .policy-row:hover {
background:#151515;
}

.main-portal .policy-row a {
display:block;
min-width:0;
overflow:hidden;
text-overflow:ellipsis;
white-space:nowrap;
color:var(--text-soft) !important;
}

.main-portal .policy-row:hover a {
color:#ffffff !important;
text-decoration:none !important;
}

.main-portal .policy-mark {
color:var(--text-dim);
font-size:14px;
line-height:1;
white-space:nowrap;
text-align:right;
}

.main-portal .policy-row:hover .policy-mark {
color:#ffffff;
}

.main-portal .social-strip {
display:grid;
grid-template-columns:repeat(4,1fr);
gap:var(--space-tight);
margin-top:var(--space-inner);
}

.main-portal .social-icon {
display:block;
height:26px;
min-width:0;
}

.main-portal .social-icon a {
display:flex !important;
align-items:center !important;
justify-content:center !important;
width:100% !important;
height:26px !important;
border:1px solid var(--edge-soft) !important;
background:#111111 !important;
color:var(--text-soft) !important;
font-size:10px !important;
font-weight:700 !important;
line-height:1 !important;
text-decoration:none !important;
text-shadow:1px 1px 0 #000 !important;
box-sizing:border-box !important;
}

.main-portal .social-icon a:hover {
background:#1c1c1c !important;
color:#ffffff !important;
border-color:#3a3a3a !important;
text-decoration:none !important;
}

/* -----------------------------------------
17. Responsive
----------------------------------------- */

@media screen and (max-width:1380px) {
.main-portal .module-grid {
grid-template-columns:minmax(0,1fr) minmax(0,1fr);
}

.main-portal .footer-grid {
grid-template-columns:minmax(0,1fr);
}
}

@media screen and (max-width:1180px) {
.main-portal .console-grid {
grid-template-columns:minmax(0,1fr);
}

.main-portal .module-grid {
grid-template-columns:minmax(0,1fr);
}

.main-portal .notice-line {
grid-template-columns:62px minmax(0,1fr);
min-height:32px;
}

.main-portal .notice-date,
.main-portal .notice-action {
display:none;
}
}

/* -----------------------------------------
Analog hover normalization
----------------------------------------- */
.main-portal a,
.main-portal button,
.main-portal .notice-line,
.main-portal .notice-line *,
.main-portal .notice-action,
.main-portal .portal-cat-anchor .portal-cat-shape,
.main-portal .portal-cat-anchor .portal-cat-label,
.main-portal .record-link,
.main-portal .record-link *,
.main-portal .archive-row,
.main-portal .archive-row *,
.main-portal .tracker-row,
.main-portal .tracker-row *,
.main-portal .guestbook-row,
.main-portal .guestbook-row * {
transition:none !important;
}