/* Core-owned CSS custom property defaults (docs/DECISIONS.md D9-1).
 *
 * The shell loads this file BEFORE the active skin's stylesheet, so a skin that
 * declares no --mg-* values at all still renders a readable site and a working
 * /manage/ area. Skins override by redefining the same names in their own
 * :root block: same specificity, later in the cascade, so the skin wins.
 *
 * Cascade order the shell builds (templates/core/base.html):
 *   1. this file                    core defaults
 *   2. skins/{active_skin}/style.css   site skin
 *   3. #content <link>              board skin (docs/SPEC-v0.3-a.md 2)
 *   4. <style id="mango-theme">     SiteSetting.theme_values
 *
 * RULES FOR THIS FILE
 * - Declarations only. No layout, no component rules, no selectors other than
 *   :root. A skin must never have to undo something the core painted here.
 * - Every name is prefixed --mg-. Unprefixed names would collide with CSS that
 *   admins paste into `html` blocks and with skin-private variables. The ROOT
 *   design system names its own tokens without a prefix (--gold-400, --sp-6,
 *   --type-h1); every one of them is renamed on the way in for that reason.
 * - The webfont import lives here, next to the font-family defaults it feeds.
 *   Splitting them would mean a token-less skin inherits a font stack whose
 *   files nobody loaded. Every stack still ends in system-ui, so a blocked or
 *   unreachable CDN degrades instead of breaking.
 *
 * SOURCE OF RECORD
 * Values come from the ROOT design system project
 * d7baae82-a86e-4cd6-895b-d4e8542157e3, files tokens/*.css. Its readme.md
 * paraphrases colours in prose and has been wrong before in an earlier design
 * system; tokens/*.css is the only source (docs/PITFALLS.md).
 */

/* ROOT display voice: Chakra Petch sets Latin, Gothic A1 carries Korean at a
   matching weight. Isamanru speaks (UI), JetBrains Mono logs (numerals and
   labels), Gowun Batang survives for lyrical long-form prose only. */
@import url("https://fonts.googleapis.com/css2?family=Chakra+Petch:wght@600;700&family=Gothic+A1:wght@500;700;800;900&family=IBM+Plex+Sans+KR:wght@400;500;600;700&family=Gowun+Batang:wght@400;700&family=JetBrains+Mono:wght@400;500;700;800&display=swap");

/* Icons — Material Symbols Outlined, addressed by NAME: the glyph is chosen by
   the ligature text inside the element, so a menu can use any of ~3,700 icons
   without anyone vendoring a file for it first.
   ⛔ The weight is pinned. Without `:wght@500` the css2 API serves the full
      VARIABLE font, which is 3.9MB against 343KB for this static instance
      (measured 2026-08-30). ROOT also asks for 2-2.5 stroke and never thinner,
      and the default 400 reads too fine beside a 2px border.
   ⚠ Unlike the text stacks above, this one does NOT degrade gracefully: with
      the CDN unreachable an icon renders as its own name ("calendar_month"
      shows the word). Visible rather than invisible, but still wrong. That is
      the accepted cost of not shipping the font (2026-08-30 사람 결정). */
@import url("https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght@500");

/* Body face. Not on Google Fonts, so it comes from the same jsDelivr host the
   rest of this file already uses (2026-08-31 사람 지시).
 *
 * ⚠ Three static weights, not a variable font: 300 / 400 / 700. The ROOT type
 * scale asks for 500 on body and 800 on display, and neither exists here — the
 * browser picks the nearest cut (500 renders as 400, 800 as 700), so copy sits
 * a shade lighter than the design system draws it. That is the trade the face
 * comes with, not a bug to hunt later.
 *
 * ⚠ The family is named for the design, the files are GongGothic. Keep the two
 * together in one place so nobody "fixes" the mismatch by editing one of them.
 *
 * IBM Plex Sans KR further down --mg-font-body is the fallback when jsDelivr is
 * unreachable, which for an installed site is somebody else's network. */
@font-face {
  font-family: "Isamanru";
  font-weight: 300;
  font-style: normal;
  font-display: swap;
  src: url("https://cdn.jsdelivr.net/gh/projectnoonnu/noonfonts_20-10@1.0/GongGothicLight.woff") format("woff");
}

@font-face {
  font-family: "Isamanru";
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  src: url("https://cdn.jsdelivr.net/gh/projectnoonnu/noonfonts_20-10@1.0/GongGothicMedium.woff") format("woff");
}

@font-face {
  font-family: "Isamanru";
  font-weight: 700;
  font-style: normal;
  font-display: swap;
  src: url("https://cdn.jsdelivr.net/gh/projectnoonnu/noonfonts_20-10@1.0/GongGothicBold.woff") format("woff");
}

:root {
  /* Color — ROOT is white-dominant, ink-drawn and gold-punctuated. Gold
     (--mg-accent) is the ONLY saturated hue in the system and is rationed to
     one element per view: the single primary button, the active nav underline,
     one rule, one chip. Gold type fails contrast on white, so --mg-link steps
     down to the gold-700 ramp and --mg-accent-text is INK, never white. */
  --mg-accent: #fad228;
  --mg-accent-hover: #efc20e;
  --mg-accent-soft: #fffbea;
  --mg-accent-text: #333a40;
  --mg-green: #1fa97f;
  --mg-green-hover: #188566;
  --mg-green-soft: #e4f5ef;
  --mg-bg: #f8fafb;
  --mg-surface: #ffffff;
  --mg-surface-tint: #f0f3f5;
  --mg-text: #333a40;
  --mg-text-muted: #6b7278;
  --mg-border: #c6d2de;
  --mg-border-strong: #33445a;
  --mg-link: #9a7605;
  --mg-link-hover: #333a40;
  --mg-danger: #d8452f;
  --mg-danger-soft: #fbe9e6;
  /* Status colours are deliberately desaturated so they never compete with the
     brand gold. Used by the prose callouts (docs/SKIN_API.md 프로즈 계약). */
  --mg-info: #3a6ea5;
  --mg-info-soft: #e8eff7;
  --mg-warn: #e08a16;
  --mg-warn-soft: #fdf1dc;
  /* Deeper and fainter ink than --mg-text / --mg-text-muted. ROOT's neutral
     ramp runs further at both ends than the two-step scale allowed for. */
  --mg-text-strong: #272d33;
  --mg-text-faint: #8e959b;
  /* One step below --mg-surface-tint: a plate inside an already tinted panel. */
  --mg-surface-sunken: #e5eaed;
  /* Gold used as material rather than as type: a highlighter wash and an
     underline. Neither is ever a text colour — gold type steps to --mg-link. */
  --mg-accent-mark: #fde68a;
  --mg-accent-line: #fbdd5c;
  /* The scan sweep's material: a gold wash that crosses a pressable surface
     once on hover. Never a looping shine (docs/SKIN_API.md 프레임 계약). */
  --mg-wash-gold: linear-gradient(120deg, rgba(250, 210, 40, 0.22) 0%, rgba(250, 210, 40, 0) 62%);

  /* Schedule categories. ROOT rations saturated colour to one element per view,
     but a calendar shows every category at once, so these are borrowed whole
     from ROOT's status and rarity ramps rather than invented. ROOT only offers
     six usable hues; 3 and 7 are the same blue family split by lightness. */
  --mg-schedule-1: #9a7605;
  --mg-schedule-2: #1fa97f;
  --mg-schedule-3: #3a6ea5;
  --mg-schedule-4: #e08a16;
  --mg-schedule-5: #b07fd4;
  --mg-schedule-6: #d8452f;
  --mg-schedule-7: #7fa3c4;
  --mg-schedule-8: #4e555b;

  /* Background — the image is set from an Attachment id by apps/core/theme.py.
     A raw URL is never accepted from the admin (D9-1). ROOT ships a paper grain
     texture, but it stays an opt-in asset: baking it in here would fight the
     admin's own background choice in /manage/theme. */
  --mg-bg-image: none;
  /* 종이 질감을 얼마나 밝힐지 (0 = 원본, 1 = 흰색). 관리자가 배경 이미지를
     올리면 그 이미지가 위 층이라 이 값은 질감에만 걸린다. */
  --mg-bg-texture-lighten: 0.3;
  --mg-bg-repeat: no-repeat;
  --mg-bg-size: cover;
  --mg-bg-position: center;
  /* ROOT lays its paper texture fixed so the grain stays put while the page
     scrolls. The admin can still override this like any other token. */
  --mg-bg-attachment: fixed;

  /* Shape — ROOT corners are soft-but-square and the ladder is tighter than the
     old one: cards 6px, inputs 3px. ROOT draws with THICK line, so the default
     container edge is 2px and shadows are hard offsets with ZERO blur, tuned to
     the frame's ink blue so shadow and stroke read as one material. */
  --mg-radius: 6px;
  --mg-radius-sm: 3px;
  /* Images sit between cards and chips on ROOT's radius ladder. */
  --mg-radius-md: 10px;
  --mg-radius-pill: 999px;
  /* Control height. ROOT sizes a pressable thing by height, not by padding, so
     a button and an input line up without either knowing the other's spacing.
     44px is the tap minimum; 46 is the design system's default control. */
  --mg-control-h: 46px;
  --mg-border-width: 2px;
  --mg-shadow: 0 1px 0 rgba(51, 58, 64, 0.1);
  /* The middle rung of ROOT's elevation ladder (rest 0 / hover 3 / press 6).
     Only a pressable thing climbs it -- see the button rules in the skin. */
  --mg-shadow-hover: 0 3px 0 rgba(51, 58, 64, 0.13);
  --mg-shadow-lift: 0 10px 0 rgba(51, 58, 64, 0.18);
  /* The one dramatic depth in the system: a printed slab, never a glow. */
  --mg-shadow-slab: 4px 4px 0 #232f3d;
  --mg-shadow-slab-soft: 3px 3px 0 rgba(51, 58, 64, 0.2);
  /* Emphasis and block edges. --mg-border-width is the container default.
     A true hairline exists only for a divider INSIDE an already bordered
     panel — ROOT has no whisper-grey edges anywhere else. */
  --mg-stroke-hair: 1px;
  --mg-stroke-1: 2px;
  --mg-stroke-2: 3px;
  --mg-stroke-3: 4px;

  /* Motion — enter slow and eased out, exit fast. Nothing bounces. */
  --mg-ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --mg-transition-fast: 160ms cubic-bezier(0.16, 1, 0.3, 1);
  --mg-transition: 240ms cubic-bezier(0.16, 1, 0.3, 1);
  /* Panels and artwork travel slower than a hover: ROOT's 420ms step. */
  --mg-transition-slow: 420ms cubic-bezier(0.16, 1, 0.3, 1);

  /* Typography — three voices. The display face hits (heavy squared geometric,
     flat terminals), the sans speaks and starts at 500 rather than 400, and the
     mono logs: kickers, dates, ids and counts, always uppercase and tracked. */
  --mg-font-heading: "Chakra Petch", "Gothic A1", "Apple SD Gothic Neo", "Malgun Gothic", system-ui, sans-serif;
  --mg-font-body: "Isamanru", "IBM Plex Sans KR", "Apple SD Gothic Neo", system-ui, sans-serif;
  --mg-font-mono: "JetBrains Mono", ui-monospace, Menlo, monospace;
  /* Lives here rather than in the skin so the @import above and the family
     that consumes it stay in one file (docs/PITFALLS.md 폰트 규칙). */
  --mg-font-icon: "Material Symbols Outlined";
  /* Lyrical long-form paragraphs only. Never a title face. */
  --mg-font-serif: "Gowun Batang", "Apple SD Gothic Neo", serif;
  /* Type scale, declared twice on purpose. The -raw pair holds the measurement;
     the plain name is an alias every consumer already uses.
     A surface that scales its own type (the page font zoom, static/core/js/
     page-zoom.js) redeclares the plain names on ITS OWN element as
     calc(-raw * scale). It cannot fold the multiplier in here: a custom
     property is substituted where it is declared, so a :root declaration that
     read a scale variable would freeze that scale at :root and inherit the
     frozen value down. Nor can it multiply the plain name by itself -- a var()
     cycle makes the declaration invalid. Hence the raw pair. */
  --mg-font-size-base-raw: 15px;
  --mg-font-size-xs-raw: 12px;
  --mg-font-size-sm-raw: 13px;
  --mg-font-size-md-raw: 17px;
  --mg-font-size-lg-raw: 25px;
  --mg-font-size-xl-raw: 34px;
  --mg-font-size-2xl-raw: clamp(30px, 3.1vw, 46px);
  --mg-font-size-base: var(--mg-font-size-base-raw);
  --mg-font-size-xs: var(--mg-font-size-xs-raw);
  --mg-font-size-sm: var(--mg-font-size-sm-raw);
  --mg-font-size-md: var(--mg-font-size-md-raw);
  --mg-font-size-lg: var(--mg-font-size-lg-raw);
  --mg-font-size-xl: var(--mg-font-size-xl-raw);
  --mg-font-size-2xl: var(--mg-font-size-2xl-raw);
  /* Reader-chosen page type scale. The default belongs here so a skin that
     draws the zoom controls never reads an undeclared name; the multiplication
     does NOT -- see the comment above the -raw pair. */
  --mg-page-font-scale: 1;
  --mg-emoticon-size: 1.5em;
  --mg-line-height: 1.68;
  /* Tracking. Mono labels are set ALL CAPS at --mg-track-widest; Korean has no
     case, so the Latin does the work of signalling a system label. */
  --mg-track-tight: -0.03em;
  --mg-track-wide: 0.06em;
  --mg-track-wider: 0.14em;
  --mg-track-widest: 0.28em;

  /* Rules — a ROOT rule is a bar, not a line. */
  --mg-bar-h: 4px;
  --mg-bar-ink: #333a40;
  --mg-bar-gold: #fad228;

  /* Translucent plates. ROOT frames a panel by putting a 2px ink edge around a
     backdrop-blurred plate rather than by filling it — the page texture stays
     faintly visible through the panel, which is what makes it read as paper. */
  --mg-surface-plate: rgba(248, 250, 251, 0.72);
  --mg-surface-plate-strong: rgba(240, 243, 245, 0.88);
  --mg-frame-blur: saturate(130%) blur(22px);
  --mg-shadow-float: 0 12px 32px rgba(51, 58, 64, 0.18);
  /* An edge drawn INSIDE the box, so a plate carries a stroke without spending
     layout on a border. No hairline variant — a faint inner line reads as fake
     material texture, which ROOT does not do. */
  --mg-frame-ring: inset 0 0 0 var(--mg-stroke-1) var(--mg-border-strong);
  /* Notched corners. The outer frame and the inner plate use different radii so
     the 2px ink edge stays an even width around the whole notch. */
  --mg-clip-notch: polygon(15px 0, 100% 0, 100% calc(100% - 15px), calc(100% - 15px) 100%, 0 100%, 0 15px);
  --mg-clip-notch-inner: polygon(13px 0, 100% 0, 100% calc(100% - 13px), calc(100% - 13px) 100%, 0 100%, 0 13px);
  /* A label tab that sits above a box: the trailing edge is cut away. */
  --mg-clip-tab: polygon(0 0, 100% 0, calc(100% - 8px) 100%, 0 100%);
  /* A cartridge bay: the notch is cut OUT of the top edge rather than off a
     corner. Same two-path trick as the notch -- outer is the stroke, inner is
     the card 2px in. */
  --mg-clip-slot: polygon(0 0, 26% 0, 26% 12px, 50% 12px, 50% 0, 100% 0, 100% 100%, 0 100%);
  --mg-clip-slot-inner: polygon(0 0, 26% 0, 26% 10px, 50% 10px, 50% 0, 100% 0, 100% 100%, 0 100%);

  /* Protection veils — artwork is veiled, never scrimmed: ROOT lays WHITE
     gradients over images so text stays black, the opposite of the usual
     dark-overlay habit. */
  --mg-veil-bottom: linear-gradient(to top, rgba(255, 255, 255, 0.96) 0%, rgba(255, 255, 255, 0.82) 38%, rgba(255, 255, 255, 0) 100%);
  --mg-veil-left: linear-gradient(to right, rgba(255, 255, 255, 0.94) 0%, rgba(255, 255, 255, 0) 62%);

  /* Spacing — 4px rhythm. Before these existed the skin used --mg-radius as a
     gap unit, which coupled corner rounding to layout density. */
  --mg-space-1: 4px;
  --mg-space-2: 8px;
  --mg-space-3: 12px;
  --mg-space-4: 16px;
  --mg-space-5: 24px;
  --mg-space-6: 32px;
  --mg-space-7: 48px;
  --mg-space-8: 64px;

  /* Layout */
  --mg-content-width: 1160px;
  /* Narrower box for the screens a visitor reads straight through -- a page and
     a novel chapter. Reading width is a measure, not real estate: a Korean
     character is about one em at body size, so 1160px ran ~65 characters per
     line against the 40 CJK characters accessibility guidance asks for. Lists,
     the home screen and profile layouts keep --mg-content-width; they are
     scanned, not read (2026-08-31 사람 결정). */
  --mg-reading-width: 960px;
  /* The lightbox is a viewer, not a column. It kept the wider value on purpose
     when the content box narrowed -- shrinking "view the original" is a loss on
     a gallery site, and it has no line length to protect. */
  --mg-lightbox-width: 1160px;
  --mg-shell-nav-width: 236px;
  /* The floating rail: a short capsule detached from the edge, not a
     full-height column. --rail-width is the column the shell reserves;
     the capsule itself is narrower and sits inset within it. */
  --mg-shell-rail-width: 6.25rem;
  --mg-shell-rail-inset: 0.75rem;
  --mg-shell-icon: 3.25rem;
  /* ROOT sets icons at 18-24px and never thinner than 2 stroke. The
     glyph size is absolute, not an em: the rail's own font-size is the
     small label scale, which would shrink every icon to ~18px. */
  --mg-icon-size: 20px;
  --mg-shell-dock-height: 64px;
  /* BGM widget (docs/plans/v0.3-c-1-bgm-widget.md §4-5). One round icon the size
     of a dock target; the YouTube frame fills that same box behind it and is
     hidden with opacity. The frame is never display:none or zero-sized — a
     frame the browser stops rendering can stop playing. */
  --mg-bgm-button-size: 2.75rem;
  --mg-bgm-panel-width: 14rem;
  --mg-shell-sidebar-width: 18rem;
  /* Page contents rail. Sized so it still clears the page gutter beside the
     1160px content box once the viewport is wide enough to have one. */
  --mg-page-toc-width: 13rem;
  --mg-avatar-size: 4.75rem;
  --mg-avatar-size-sm: 3.25rem;
  --mg-profile-aside-width: 13rem;

  /* Novel reader (docs/SKIN_API.md). Long-form copy runs looser than the rest
     of the site, so the reader keeps its own line height rather than
     inheriting --mg-line-height. */
  --mg-reader-font-scale: 1;
  --mg-reader-line-height: 1.8;
  --mg-reader-align: left;
  --mg-reader-font-family: var(--mg-font-body);
  --mg-reader-font-serif: "Gowun Batang", Georgia, "Times New Roman", serif;
  --mg-reader-width: var(--mg-reading-width);
}

/* ROOT zeroes every duration token at the :root level rather than asking each
   component to opt out. Declarations only, still no new selectors. */
@media (prefers-reduced-motion: reduce) {
  :root {
    --mg-transition-fast: 0ms;
    --mg-transition: 0ms;
    --mg-transition-slow: 0ms;
  }
}
