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

편집 요약 없음
편집 요약 없음
태그: 수동 되돌리기
 
(같은 사용자의 중간 판 6개는 보이지 않습니다)
1번째 줄: 1번째 줄:
/* =========================================
/* =========================================
COASTLINE: BLACK ICE - CategoryNav
COASTLINE: BLACK ICE - MainPage
대문 카테고리 네비 SVG 생성기
대문 전용 스타일
Full portal layout + SVG category navigation
========================================= */
========================================= */


7번째 줄: 8번째 줄:
설계 목적
설계 목적
-----------------------------------------
-----------------------------------------
이 파일은 대문 카테고리 네비를 MediaWiki 본문 위키문법이 아니라
이 파일은 대문 전용 화면 전체를 담당한다.
JavaScript + SVG로 생성한다.


SVG를 쓰는가
이전 Test.css는 실험용이었다.
-----------------------------------------
그 안에는 대문 레이아웃, 공지, 로고, 이미지 피드, 방명록 더미,
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;
}


1. span 안에 [[문서|라벨]]을 넣는 방식
.main-portal .notice-line:hover .notice-action {
  - MediaWiki가 내부에 <a>를 자동 생성한다.
border-color:#606060;
  - span은 모양을 담당하고, a는 클릭을 담당하게 된다.
background:#181818;
  - 실제 클릭 영역, 호버 영역, 시각 형태가 서로 분리된다.
color:#ffffff;
}


2. <a class="...">를 본문에 직접 넣는 방식
/* -----------------------------------------
  - MediaWiki 본문에서 안정적으로 먹히지 않았다.
3. Top logo
  - 위키 문법 처리와 충돌해 코드가 무효화되는 문제가 있었다.
----------------------------------------- */


3. clip-path + ::after 방식
.main-portal .lab-logo {
  - clip-path가 검은 획 pseudo-element까지 잘라먹었다.
width:100%;
  - 왼쪽 항목의 획이 사라지거나 일부만 보였다.
height:auto;
margin:0 0 var(--space-block);
padding:10px 0 8px;
text-align:center;
line-height:0;
background:#0b0b0b;
}


4. 버튼을 음수 margin으로 겹치는 방식
.main-portal .lab-logo a,
  - 사선 빈 영역은 메웠지만, 호버가 옆 칸으로 넘어갔다.
.main-portal .lab-logo .mw-file-description {
display:inline-block;
line-height:0;
}


5. hover overlay / divider overlay 방식
.main-portal .lab-logo img,
  - CSS grid, absolute 좌표, 프로젝트 가변폭이 서로 다른 기준을 가져서
.main-portal .lab-logo .mw-file-element {
    호버 면과 검은 획이 계속 어긋났다.
width:760px;
max-width:90%;
height:auto;
filter:
grayscale(1)
contrast(1.04)
brightness(1.02);
}


SVG 방식의 장점
/* -----------------------------------------
-----------------------------------------
4. Category navigation mount
SVG에서는 각 항목을 polygon으로 만든다.
----------------------------------------- */
따라서 다음 요소들이 모두 같은 좌표계를 사용한다.


- 보이는 버튼 면
.main-portal [data-component="category-nav"] {
- 실제 hover 면
display:block;
- 실제 click 면
width:100%;
- 검은 구분선
margin:0 0 var(--space-block);
}


즉, 이전처럼 사각형 박스를 사선처럼 보이게 흉내 내는 방식이 아니라,
/* -----------------------------------------
처음부터 사선 polygon을 생성한다.
5. Category navigation - SVG version
----------------------------------------- */


JS의 책임
/*
-----------------------------------------
SVG 방식에서는 각 항목이 polygon이다.
JS는 구조와 좌표만 만든다.
보이는 면, hover 면, click 면, 검은 구분선이 같은 SVG 좌표계를 사용한다.
색상, 호버 색, 글자 스타일은 MainPage.css가 담당한다.


좌표는 현재 nav 폭을 읽어서 계산한다.
CSS는 색상, 호버, 텍스트 스타일, 외곽 표면만 담당한다.
프로젝트 버튼은 좌우 카테고리 3개씩을 제외한 남은 중앙 폭을 차지한다.
버튼 폭과 사선 좌표는 CategoryNav.js가 계산한다.
*/
*/


(function () {
.portal-category-nav {
    'use strict';
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;
}


    var SVG_NS = 'http://www.w3.org/2000/svg';
.main-portal .statement-plate h2 {
    var XLINK_NS = 'http://www.w3.org/1999/xlink';
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;
}


    다국어 확장 시에는 여기의 label/title을 직접 바꾸기보다
.main-portal .statement-plate p:last-child {
    getLabel(), getTitle()에서 window.LANG, window.CAT_LINKS 또는
margin-bottom:0;
    별도 MainPageConfig.js를 읽게 하면 된다.
}
    */


    var CATEGORY_NAV_ITEMS = [
/* -----------------------------------------
        {
9. Guestbook preview
            key: 'history',
----------------------------------------- */
            label: '역사적 사건',
            title: '역사적_사건',
            type: 'left-start'
        },
        {
            key: 'settings',
            label: '설정',
            title: '설정',
            type: 'left-mid'
        },
        {
            key: 'nations',
            label: '국가 및 조합',
            title: '국가_및_조합',
            type: 'left-end'
        },
        {
            key: 'project',
            label: '프로젝트',
            title: '프로젝트:소개',
            type: 'project'
        },
        {
            key: 'corporations',
            label: '기업 및 공동체',
            title: '기업_및_공동체',
            type: 'right-start'
        },
        {
            key: 'military',
            label: '군, 정치집단',
            title: '군_정치집단',
            type: 'right-mid'
        },
        {
            key: 'personnel',
            label: '인물',
            title: '인물',
            type: 'right-end'
        }
    ];


    /*
.main-portal .screen-title {
    언어 감지
height:var(--screen-title-h);
    -----------------------------------------
min-height:var(--screen-title-h);
    기존 Common.js에 getCurrentLang() 함수가 있을 수 있다.
margin:0 0 var(--space-inner);
    이 파일은 단독으로도 깨지지 않아야 하므로 fallback을 둔다.
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;
}


    function getCurrentLanguageSafe() {
.main-portal .screen-title span:last-child {
        if (typeof window.getCurrentLang === 'function') {
color:var(--text-dim);
            return window.getCurrentLang();
font-size:9px;
        }
}


        var langData = document.getElementById('clbi-lang-data');
.main-portal .guestbook-device {
flex:1 1 auto;
min-height:0;
display:flex;
flex-direction:column;
gap:var(--space-inner);
}


        if (langData) {
.main-portal .guestbook-messages {
            return langData.getAttribute('data-lang') || 'ko';
flex:1 1 auto;
        }
min-height:0;
border-top:1px solid var(--edge-soft);
border-bottom:1px solid var(--edge-soft);
overflow:hidden;
}


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


    /*
.main-portal .guestbook-message:last-child {
    경로 생성
border-bottom:none;
    -----------------------------------------
}
    기존 Common.js에 buildWikiPath(title)가 있으면 그것을 사용한다.
    없으면 /index.php/문서명 형식으로 fallback한다.
    */


    function buildPath(title) {
.main-portal .guestbook-message:hover {
        if (typeof window.buildWikiPath === 'function') {
background:#111111;
            return window.buildWikiPath(title);
}
        }


        return '/index.php/' + encodeURI(String(title || '').replace(/ /g, '_'));
.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;
}


    기존 LANG.js와 CAT_LINKS에는 모든 새 항목이 정리되어 있지 않을 수 있다.
.main-portal .guestbook-time {
    예를 들어 '설정', '프로젝트'는 기존 세계관 카테고리 세트와 다르다.
color:var(--text-dim);
white-space:nowrap;
}


    부분 번역을 섞으면 언어별 네비 폭과 의미가 흔들릴 수 있으므로,
.main-portal .guestbook-text {
    다국어 구조가 확정되기 전까지는 fallback label을 유지한다.
color:var(--text-soft);
    */
font-size:10px;
line-height:1.45;
word-break:keep-all;
}


    function getLabel(item, lang) {
.main-portal .guestbook-form {
        if (lang === 'ko') {
display:grid;
            return item.label;
grid-template-columns:minmax(0,1fr) 48px;
        }
gap:var(--space-tight);
}


        return item.label;
.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;
    이상적인 카테고리 폭은 188px이다.
}
    이전 기준은 163px이었고, 여기서 프로젝트를 제외한 6개 버튼의 폭을
    약 15% 늘린 값이다.


    163 * 1.15 = 187.45 이므로 렌더링 안정성을 위해 188px로 둔다.
.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;
}


    다만 실제 본문 폭이 좁으면 188px 6개 + 프로젝트 최소폭이 들어가지 않는다.
.main-portal .guestbook-submit:hover {
    그럴 때는 cellW를 줄여서 전체가 깨지지 않게 한다.
background:#222222;
color:#ffffff;
}


    계산 원칙:
/* -----------------------------------------
    - 좌측 3개와 우측 3개는 같은 폭이다.
10. Ticker
    - 프로젝트는 남은 중앙 폭을 차지한다.
----------------------------------------- */
    - 사선 cut 값은 cellW에 따라 너무 커지거나 작아지지 않게 제한한다.
    */


    function calculateGeometry(width) {
.main-portal .ticker {
        var h = 30;
height:var(--ticker-h);
        var idealCell = 188;
margin:0 0 var(--space-inner);
        var minCell = 96;
padding:0 var(--pad-panel);
        var minProject = 180;
color:#bdbdbd;
        var usableWidth = Math.max(320, width);
background:#171717;
font-size:10px;
line-height:var(--ticker-h);
white-space:nowrap;
overflow:hidden;
text-overflow:ellipsis;
text-shadow:1px 1px 0 #000;
}


        var cellW = idealCell;
/* -----------------------------------------
11. Module grid
----------------------------------------- */


        if ((cellW * 6) + minProject > usableWidth) {
.main-portal .module-grid {
            cellW = Math.floor((usableWidth - minProject) / 6);
display:grid;
            cellW = Math.max(minCell, cellW);
grid-template-columns:minmax(0,1fr) minmax(0,1fr) 280px;
        }
gap:var(--space-inner);
align-items:stretch;
}


        var projectW = usableWidth - (cellW * 6);
.main-portal .panel,
.main-portal .instrument-panel {
min-width:0;
height:100%;
display:flex;
flex-direction:column;
}


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


        /*
/* -----------------------------------------
        projectW를 보정했을 경우 전체 폭이 넘칠 수 있다.
12. Record modules
        그 상황에서는 viewBox를 전체 필요 폭으로 잡고,
----------------------------------------- */
        SVG가 가로로 스케일되게 둔다.
        일반 대문 폭에서는 이 분기가 거의 발생하지 않는다.
        */
        var totalW = (cellW * 6) + projectW;


        var cut = Math.round(cellW * 0.061);
.main-portal .record-list {
        cut = Math.max(6, Math.min(10, cut));
margin:0 !important;
padding:0 !important;
list-style:none !important;
}


        var x0 = 0;
.main-portal .record-list li {
        var x1 = cellW;
display:grid;
        var x2 = cellW * 2;
grid-template-columns:minmax(0,1fr) auto;
        var x3 = cellW * 3;
align-items:center;
        var x4 = x3 + projectW;
gap:var(--space-block);
        var x5 = x4 + cellW;
height:var(--row-h);
        var x6 = x4 + (cellW * 2);
min-height:var(--row-h);
        var x7 = x4 + (cellW * 3);
margin:0;
padding:0 2px;
border-bottom:1px solid var(--edge-soft);
color:var(--text-soft);
line-height:1.25;
}


        return {
.main-portal .record-list li:first-child {
            width: totalW,
border-top:1px solid var(--edge-soft);
            height: h,
}
            cellW: cellW,
            projectW: projectW,
            cut: cut,
            x: [x0, x1, x2, x3, x4, x5, x6, x7]
        };
    }


    /*
.main-portal .record-list li:hover {
    polygon 좌표
background:#151515;
    -----------------------------------------
color:#ffffff;
    요구 형태:
}
    왼쪽군:   \ \ \
    프로젝트: \ PROJECT /
    오른쪽군: / / /


    좌표 설명:
.main-portal .record-list li span:first-child {
    - \ 경계는 위쪽 x가 왼쪽, 아래쪽 x가 오른쪽이다.
min-width:0;
    - / 경계는 위쪽 x가 오른쪽, 아래쪽 x가 왼쪽이다.
overflow:hidden;
text-overflow:ellipsis;
white-space:nowrap;
}


    예시:
.main-portal .meta {
    왼쪽 첫 경계 \ :
color:var(--text-dim);
    top    x1 - cut
white-space:nowrap;
    bottom x1
font-size:9px;
line-height:1;
}


    오른쪽 첫 경계 / :
.main-portal .record-list li:hover .meta {
    top    x4
color:#c0c0c0;
    bottom x4 - cut
}
    */


    function getSegmentPoints(index, g) {
/* -----------------------------------------
        var x = g.x;
13. Status tags
        var c = g.cut;
----------------------------------------- */
        var h = g.height;


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


        if (index === 1) {
.main-portal .status-tag.is-progress {
            return [
border-color:#6d5b2c;
                [x[1] - c, 0],
background:#19170f;
                [x[2] - c, 0],
}
                [x[2], h],
                [x[1], h]
            ];
        }


        if (index === 2) {
.main-portal .status-tag.is-review {
            return [
border-color:#36515d;
                [x[2] - c, 0],
background:#10171a;
                [x[3] - c, 0],
}
                [x[3], h],
                [x[2], h]
            ];
        }


        if (index === 3) {
.main-portal .status-tag.is-wait {
            return [
border-color:#484848;
                [x[3] - c, 0],
background:#151515;
                [x[4], 0],
}
                [x[4] - c, h],
                [x[3], h]
            ];
        }


        if (index === 4) {
.main-portal .status-tag.is-done {
            return [
border-color:#3d5c38;
                [x[4], 0],
background:#111811;
                [x[5], 0],
}
                [x[5] - c, h],
                [x[4] - c, h]
            ];
        }


        if (index === 5) {
.main-portal .status-tag.is-alert {
            return [
border-color:#6a3933;
                [x[5], 0],
background:#1b1110;
                [x[6], 0],
}
                [x[6] - c, h],
                [x[5] - c, h]
            ];
        }


        return [
/* -----------------------------------------
            [x[6], 0],
14. Team work module
            [x[7], 0],
----------------------------------------- */
            [x[7], h],
            [x[6] - c, h]
        ];
    }


    function pointsToString(points) {
.main-portal .team-panel .panel-body {
        return points.map(function (point) {
position:relative;
            return point[0] + ',' + point[1];
padding-left:15px;
        }).join(' ');
}
    }


    function getTextX(index, g) {
.main-portal .team-panel .panel-body::before {
        var x = g.x;
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;
}


        if (index === 0) return (x[0] + x[1]) / 2;
.main-portal .team-panel .titlebar > span:last-child {
        if (index === 1) return (x[1] + x[2]) / 2;
color:#c6c6c6;
        if (index === 2) return (x[2] + x[3]) / 2;
}
        if (index === 3) return (x[3] + x[4]) / 2;
        if (index === 4) return (x[4] + x[5]) / 2;
        if (index === 5) return (x[5] + x[6]) / 2;
        return (x[6] + x[7]) / 2;
    }


    function createSvgElement(name) {
/* -----------------------------------------
        return document.createElementNS(SVG_NS, name);
15. Status instrument
    }
----------------------------------------- */


    /*
.main-portal .instrument-grid {
    SVG 링크 생성
display:grid;
    -----------------------------------------
grid-template-columns:minmax(0,1fr) minmax(0,1fr);
    SVG <a> 안에 polygon과 text를 함께 넣는다.
gap:var(--space-inner);
    polygon이 hover/click 면이고, text는 pointer-events:none으로 둔다.
margin:0 0 var(--space-inner);
    */
}


    function createSegment(item, index, g, lang) {
.main-portal .gauge {
        var anchor = createSvgElement('a');
height:var(--gauge-h);
        var polygon = createSvgElement('polygon');
min-width:0;
        var text = createSvgElement('text');
padding:6px;
        var href = buildPath(item.title);
display:flex;
        var label = getLabel(item, lang);
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);
}


        anchor.setAttribute('class', 'portal-cat-anchor');
.main-portal .gauge:hover {
        anchor.setAttribute('href', href);
background:#0d0d0d;
        anchor.setAttributeNS(XLINK_NS, 'xlink:href', href);
}
        anchor.setAttribute('aria-label', label);
        anchor.setAttribute('data-category-key', item.key);


        polygon.setAttribute('class', 'portal-cat-shape');
.main-portal .gauge b {
        polygon.setAttribute('points', pointsToString(getSegmentPoints(index, g)));
display:block;
color:#f0f0f0;
font-family:Consolas, Monaco, monospace !important;
font-size:18px;
line-height:1;
text-shadow:1px 1px 0 #000;
}


        text.setAttribute('class', item.key === 'project'
.main-portal .gauge span {
            ? 'portal-cat-label portal-cat-project-label'
display:block;
            : 'portal-cat-label'
margin-top:5px;
        );
color:var(--text-dim);
        text.setAttribute('x', getTextX(index, g));
font-size:9px;
        text.setAttribute('y', g.height / 2 + 1);
line-height:1;
        text.textContent = label;
}


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


        return anchor;
.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;
    -----------------------------------------
}
    polygon 경계와 같은 x 값을 사용해 line을 만든다.
    그러므로 호버 면과 검은 획이 다른 좌표계를 쓰지 않는다.
    */


    function createDivider(x1, y1, x2, y2, side) {
.main-portal .instrument-line:hover {
        var line = createSvgElement('line');
background:#151515;
}


        line.setAttribute('class', 'portal-cat-divider portal-cat-divider-' + side);
.main-portal .instrument-label {
        line.setAttribute('x1', x1);
color:var(--text-dim);
        line.setAttribute('y1', y1);
white-space:nowrap;
        line.setAttribute('x2', x2);
}
        line.setAttribute('y2', y2);


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


    function appendDividers(svg, g) {
.main-portal .instrument-line:hover .instrument-value {
        var x = g.x;
color:#ffffff;
        var c = g.cut;
}
        var h = g.height;


        /*
/* -----------------------------------------
        왼쪽 3개 구분선: \ 방향
16. Footer split
        */
----------------------------------------- */
        svg.appendChild(createDivider(x[1] - c, 0, x[1], h, 'left'));
        svg.appendChild(createDivider(x[2] - c, 0, x[2], h, 'left'));
        svg.appendChild(createDivider(x[3] - c, 0, x[3], h, 'left'));


        /*
.main-portal .footer-grid {
        오른쪽 3개 구분선: / 방향
display:grid;
        */
grid-template-columns:minmax(0,1fr) 300px;
        svg.appendChild(createDivider(x[4], 0, x[4] - c, h, 'right'));
gap:var(--space-inner);
        svg.appendChild(createDivider(x[5], 0, x[5] - c, h, 'right'));
margin-top:var(--space-inner);
        svg.appendChild(createDivider(x[6], 0, x[6] - c, h, 'right'));
align-items:stretch;
    }
}


    function clearNode(node) {
.main-portal .footer-grid .panel {
        while (node.firstChild) {
height:100%;
            node.removeChild(node.firstChild);
}
        }
    }


    /*
.main-portal .manual-panel {
    실제 SVG 렌더링
margin-top:0;
    -----------------------------------------
}
    nav의 현재 폭을 읽고, 그 폭에 맞춰 viewBox와 polygon을 재생성한다.
    ResizeObserver가 이 함수를 다시 호출하면 폭 변화에도 대응한다.
    */


    function renderSvgIntoNav(nav) {
.main-portal .sample-row {
        var rect = nav.getBoundingClientRect();
display:grid;
        var measuredWidth = Math.floor(rect.width || nav.clientWidth || 0);
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);
}


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


        var lang = getCurrentLanguageSafe();
.main-portal .sample-row:hover {
        var g = calculateGeometry(measuredWidth);
background:#151515;
        var svg = createSvgElement('svg');
}


        svg.setAttribute('class', 'portal-category-svg');
.main-portal .sample-label {
        svg.setAttribute('viewBox', '0 0 ' + g.width + ' ' + g.height);
color:var(--text-dim);
        svg.setAttribute('width', g.width);
font-size:10px;
        svg.setAttribute('height', g.height);
line-height:1.25;
        svg.setAttribute('preserveAspectRatio', 'none');
}
        svg.setAttribute('aria-hidden', 'false');


        CATEGORY_NAV_ITEMS.forEach(function (item, index) {
.main-portal .sample-value {
            svg.appendChild(createSegment(item, index, g, lang));
color:var(--text-soft);
        });
font-size:10px;
line-height:1.35;
}


        appendDividers(svg, g);
.main-portal .sample-row:hover .sample-value {
color:#ffffff;
}


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


    function scheduleRender(nav) {
.main-portal .policy-row {
        if (!nav || nav.__categoryNavFrame) {
display:grid;
            return;
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;
}


        nav.__categoryNavFrame = window.requestAnimationFrame(function () {
.main-portal .policy-row:last-child {
            nav.__categoryNavFrame = null;
border-bottom:none;
            renderSvgIntoNav(nav);
}
        });
    }


    /*
.main-portal .policy-row:hover {
    mount 렌더링
background:#151515;
    -----------------------------------------
}
    본문에는 다음 자리만 둔다.


    <div data-component="category-nav"></div>
.main-portal .policy-row a {
display:block;
min-width:0;
overflow:hidden;
text-overflow:ellipsis;
white-space:nowrap;
color:var(--text-soft) !important;
}


    JS는 그 안에 nav.portal-category-nav를 만들고,
.main-portal .policy-row:hover a {
    SVG를 렌더링한다.
color:#ffffff !important;
    */
text-decoration:none !important;
}


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


        if (mount.__categoryNavObserver) {
.main-portal .policy-row:hover .policy-mark {
            mount.__categoryNavObserver.disconnect();
color:#ffffff;
            mount.__categoryNavObserver = null;
}
        }


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


        var nav = document.createElement('nav');
.main-portal .social-icon {
        nav.className = 'portal-category-nav';
display:block;
        nav.setAttribute('aria-label', 'Main categories');
height:26px;
min-width:0;
}


        mount.appendChild(nav);
.main-portal .social-icon a {
        mount.setAttribute('data-category-nav-rendered', '1');
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;
}


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


        /*
/* -----------------------------------------
        nav 폭이 바뀌면 SVG 좌표를 다시 계산한다.
17. Responsive
        이것이 CSS absolute overlay 방식과 다른 점이다.
----------------------------------------- */
        폭 변화가 생겨도 JS가 polygon 좌표를 다시 맞춘다.
        */
        if (window.ResizeObserver) {
            var observer = new ResizeObserver(function () {
                scheduleRender(nav);
            });


            observer.observe(nav);
@media screen and (max-width:1380px) {
            mount.__categoryNavObserver = observer;
.main-portal .module-grid {
        } else {
grid-template-columns:minmax(0,1fr) minmax(0,1fr);
            window.addEventListener('resize', function () {
}
                scheduleRender(nav);
            });
        }
    }


    function renderAllCategoryNavs(root) {
.main-portal .footer-grid {
        var scope = root || document;
grid-template-columns:minmax(0,1fr);
        var mounts = scope.querySelectorAll('[data-component="category-nav"]');
}
}


        Array.prototype.forEach.call(mounts, function (mount) {
@media screen and (max-width:1180px) {
            renderCategoryNavMount(mount);
.main-portal .console-grid {
        });
grid-template-columns:minmax(0,1fr);
    }
}


    /*
.main-portal .module-grid {
    초기화
grid-template-columns:minmax(0,1fr);
    -----------------------------------------
}
    1. 일반 페이지 로드 시 실행
    2. MediaWiki SPA/동적 본문 갱신 시 실행
    3. 필요할 때 window.CLBI.categoryNav.init()으로 수동 재실행 가능
    */


    window.CLBI = window.CLBI || {};
.main-portal .notice-line {
    window.CLBI.categoryNav = {
grid-template-columns:62px minmax(0,1fr);
        init: renderAllCategoryNavs,
min-height:32px;
        renderMount: renderCategoryNavMount
}
    };


    if (document.readyState === 'loading') {
.main-portal .notice-date,
        document.addEventListener('DOMContentLoaded', function () {
.main-portal .notice-action {
            renderAllCategoryNavs(document);
display:none;
        });
}
    } else {
}
        renderAllCategoryNavs(document);
    }


    if (window.mw && mw.hook) {
/* -----------------------------------------
        mw.hook('wikipage.content').add(function ($content) {
Analog hover normalization
            var root = $content && $content[0] ? $content[0] : document;
----------------------------------------- */
            renderAllCategoryNavs(root);
.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;
}