/* Component styles — ported from docs/reference/prototype.html. Prefer these class names in templates.
   App-specific additions belong in apps/<app>/static/<app>/<app>.css (no inline style attributes: CSP). */
*{box-sizing:border-box}
html{font-size:100%;scroll-behavior:smooth;scroll-padding-top:calc(var(--hh,82px) + env(safe-area-inset-top,0px) + 16px)}
/* Error-summary links and #field jumps land on the control; this keeps its label (above it)
   clear of the sticky header too, at any text size (WCAG 2.4.11). In em: the label scales. */
form :is(fieldset,input,select,textarea){scroll-margin-top:3.5em}
body{margin:0;background:var(--bg);color:var(--ink);font-family:var(--sans);font-weight:400;font-size:1rem;line-height:1.75;letter-spacing:-.01em;word-break:keep-all;overflow-wrap:break-word;overflow-wrap:anywhere;-webkit-font-smoothing:antialiased}
/* overflow-wrap:anywhere (break-word kept for Safari < 15.4): lines break exactly as with
   break-word — a Korean word (keep-all) splits only when it alone is wider than the line —
   but the longest word no longer sets the min-content width. So grid/flex items, auto
   tracks and fit-content boxes shrink to the window instead of pushing the page sideways
   (390px phone at 200% with large text = 195 CSS px; D17). Wider windows lay out the same:
   the change only acts where text used to overflow. Tables keep break-word: their columns
   would otherwise squeeze to one syllable; a wide table scrolls in its .scroll box —
   and so do controls: a button beside a growing field (검색 "찾기") keeps its word whole. */
:is(table,button,.btn,select,input,textarea,summary){overflow-wrap:break-word}
a{color:inherit}
button,input,select,textarea{font:inherit;color:inherit}
/* Two-tone focus ring: outline in --focus with a --bg halo, visible on any surface (WCAG 2.4.7/1.4.11). */
:focus-visible{outline:3px solid var(--focus);outline-offset:2px;box-shadow:0 0 0 2px var(--bg);border-radius:8px}
#main:focus,#main:focus-visible{outline:none}
@media (max-width:640px){.page-head .ghost-t{display:none}}
h1,h2,h3,h4{font-family:var(--serif);text-wrap:balance;line-height:1.25;margin:0}
p{margin:0}
.wrap{max-width:1240px;margin:0 auto;padding-inline:clamp(16px,4vw,40px)}
.skip{position:absolute;left:12px;top:-60px;background:var(--yellow);color:var(--on-yellow);padding:10px 16px;border-radius:10px;z-index:200;font-weight:700;text-decoration:none}
.skip:focus{top:12px}
.skip+.skip:focus{left:auto;right:12px}
.eyebrow{font-family:var(--mono);font-style:italic;font-weight:600;font-size:.95rem;letter-spacing:.04em;color:var(--muted)}
/* Latin keeps Cormorant's real italic; Hangul falls back to Noto Serif KR, which has no italic
   face, and stays upright instead of a sheared fake (VP4-03). */
:is(.eyebrow,.hero .kick span,.cover .vol){font-synthesis-style:none}
.eyebrow.k{font-family:var(--sans);font-style:normal;font-size:.78rem;letter-spacing:.12em;font-weight:500}
.help{font-size:.86rem;color:var(--muted)}
.ex-tag{display:inline-flex;align-items:center;gap:6px;font-size:.78rem;font-weight:600;color:var(--yellow-ink);background:var(--yellow-soft);padding:3px 12px;border-radius:999px}

/* 밝은 톤 영역: 어두운 토큰을 밝은 값으로 덮어씀 */
.a11y,.hero,.page-head,.bigmeter,.auth-side{--on-deep:var(--ink);--on-deep-2:var(--ink-2);--dl:color-mix(in srgb,var(--ink) 18%,transparent)}
/* ── 상단 보기 설정 ── */
.a11y{background:var(--surface);color:var(--on-deep);font-size:.8rem;border-bottom:1px solid var(--line)}
.a11y .wrap{display:flex;flex-wrap:wrap;gap:6px 18px;align-items:center;justify-content:space-between;padding-block:5px}
.a11y .l{color:var(--muted)}
.a11y .r{display:flex;flex-wrap:wrap;align-items:center;gap:6px 10px}
/* Each group is one quiet segmented track (label + options) so the bar reads as a utility,
   not as a row of calls to action; the header below stays the first thing the eye meets. */
.a11y .grp{display:flex;gap:2px;align-items:center;background:var(--sunk);border-radius:999px;padding:2px}
.a11y .grp>span:first-child{padding-inline:10px 4px}
.a11y .grp>span{color:var(--muted)}
/* Desktop: 28px segments; a ::before extender keeps a 44px hit area (--target-min). */
.a11y button{position:relative;background-color:transparent;border:1px solid transparent;color:var(--ink-2);border-radius:999px;padding:0 11px;cursor:pointer;min-height:28px;transition:transform var(--dur-ui) var(--ease-out),background-color var(--dur-ui) ease,color var(--dur-ui) ease}
.a11y button:hover{color:var(--ink);background-color:color-mix(in srgb,var(--ink) 7%,transparent)}
.a11y button::before,.a11y .adm-chip::before{content:"";position:absolute;inset:-8px -1px}
.a11y .adm-chip{position:relative}
.a11y-staff{display:flex;flex-wrap:wrap;align-items:center;gap:6px 10px}
.a11y button:active{transform:scale(.97)}
/* background-color, never the background shorthand, in these rules: the shorthand would reset
   the touch-width background-clip (D15, below). */
/* Pressed: a raised chip in the page colour — outline, lift and heavier strokes, so the
   choice never rests on colour alone. -webkit-text-stroke thickens the glyphs without changing
   their advance width, so the toolbar never shifts when the choice changes (TZ-07). */
.a11y button[aria-pressed="true"]{background-color:var(--surface);color:var(--ink);border-color:var(--line-strong);box-shadow:0 1px 2px color-mix(in srgb,var(--ink) 14%,transparent);-webkit-text-stroke:.03em currentColor}
@media (max-width:1080px){
  /* Touch widths: the segments still look like 36px chips inside the quiet 40px track, but
     each button's own box is --target-min tall (D15: target-size checks measure the element,
     not a pseudo-element). A transparent 4px top and bottom border with background-clip
     keeps the fill to the 36px chip, the pressed ring is an inset shadow inside it, and
     margin-block:-4px leaves the track's height as it was. The staff chip keeps a ::before. */
  .a11y button{min-height:var(--target-min);margin-block:-4px;border:4px solid transparent;border-inline-width:0;background-clip:padding-box}
  .a11y button::before{content:none}
  .a11y button[aria-pressed="true"]{border-color:transparent;box-shadow:inset 0 0 0 1px var(--line-strong)}
  .a11y .adm-chip{min-height:36px}
  .a11y .adm-chip::before{inset:-4px 0}
  .a11y .r{gap:6px 10px}
}
@media (max-width:640px){
  .a11y .l{display:none}
  /* Two fixed rows: the text-size group fills row 1 (equal buttons), the motion toggle sits
     at the end of row 2. The row count depends only on the viewport and the reader's text
     size, never on which font has loaded (a font swap used to re-wrap the bar and shift the
     whole page, CLS 0.3). */
  .a11y .r{width:100%;flex-wrap:wrap;justify-content:flex-end}
  .a11y .r>.grp:first-child{flex:1 1 100%}
  .a11y .r>.grp:first-child button{flex:1 1 0;white-space:nowrap;padding-inline:6px}
  .a11y .grp-motion{flex:none}
  .a11y .grp-motion button{white-space:nowrap}
  .a11y .grp>span{margin-right:0}
  /* Staff chips (관리자, 이 페이지 고치기) get their own row so the reader row keeps its room. */
  .a11y-staff{flex:1 1 100%;justify-content:flex-end;order:1}
}
/* The three text sizes (ADR-0017 decision 11) and the group's word fit one row at every
   size from 320px: 아주 크게 (130%) needs about 230px of the 288px. The two-column grid
   the four sizes needed at 150% (RD-19) put 아주 크게 alone on a row of its own at 320
   (render check 2026-10-14), so it is gone. */
/* Wider phones: one row (글자, the four sizes, then the motion toggle) once it fits with room
   to spare for wider system fonts: 460px at normal text, 540px at large. Narrower, or at the
   two largest sizes, the two-row layout above stays (reflow, WCAG 1.4.10).
   390–459px at normal text (most phones): one row too, with the group's word 글자 visually
   hidden (it still names the group; the size words say what they set). Two rows there cost
   46px and took the plaza's second row and a poem's first lines off a 390x844 screen
   (PAR-03, RD-01). */
@media (min-width:390px) and (max-width:459px){
  :root:not([data-font-scale$="large"]) .a11y .r>.grp:first-child{flex:1 1 0;min-width:max-content}
  :root:not([data-font-scale$="large"]) .a11y .r>.grp:first-child>span:first-child{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;padding:0}
}
@media (min-width:460px) and (max-width:640px){
  :root:not([data-font-scale$="large"]) .a11y .r>.grp:first-child{flex:1 1 0;min-width:max-content}
}
@media (min-width:540px) and (max-width:640px){
  :root[data-font-scale="large"] .a11y .r>.grp:first-child{flex:1 1 0;min-width:max-content}
}

/* ── 떠 있는 헤더 ── */
header.site{position:sticky;top:env(safe-area-inset-top,0px);z-index:60;padding-block:12px}
/* Only the pill takes presses: the header box's clear 12px above and below it showed the page
   through but swallowed taps on links scrolled under it (footer genre links; D9, WCAG 2.5.8). */
header.site{pointer-events:none}
header.site .bar{pointer-events:auto}
/* Phones/tablets with JS: the header steps away while the reader scrolls down and comes back
   on any scroll up, at the top, on focus or while its menu is open (site.js "header away").
   The screen is the reader's (largest text: the header was ~1/8 of it), and nothing the page
   scrolled under it is covered — footer links included (D9, WCAG 2.4.11, 2.5.8). Reduced
   motion turns the slide into an instant swap (the rule further down). Without JS it stays. */
@media (max-width:1080px) and (min-height:561px){
  .js header.site{transition:transform 220ms var(--ease-out)}
  .js header.site[data-away]:not(:focus-within){transform:translateY(calc(-100% - env(safe-area-inset-top,0px)))}
  /* Stepped away, it takes no presses either: a tap while it slides out lands on the page. */
  .js header.site[data-away]:not(:focus-within) .bar{pointer-events:none}
}
.bar{display:flex;align-items:center;gap:14px;background:var(--glass);backdrop-filter:blur(18px) saturate(1.4);-webkit-backdrop-filter:blur(18px) saturate(1.4);border:1px solid var(--line);border-radius:999px;padding:8px 8px 8px 18px;box-shadow:0 10px 30px -18px var(--shadow)}
.logo{display:flex;align-items:center;gap:10px;text-decoration:none;flex-shrink:0}
.logo svg{width:34px;height:34px}
.logo b{font-family:var(--serif);font-weight:600;font-size:1.35rem;letter-spacing:-.03em;line-height:1}
/* Tagline: 12px at least in --ink-2 (prototype .62rem/--muted was 9.9px; RD-10, MA-22). */
.logo small{display:block;font-size:.75rem;color:var(--ink-2);letter-spacing:.08em;line-height:1.2}
/* Below ~260px (400% zoom of a narrow window) the tagline gives its room to the 메뉴 button (RD-15). */
@media (max-width:260px){.logo small{display:none}}
/* Below 260px (a 390px phone at 150-200% zoom; WCAG 1.4.10 asks for 320px, the persona
   catalogue for 195px, D17) the page must not scroll sideways. Control labels may then split
   a word that is wider than the control (a button inside a padded card at 130% text), and
   buttons give up some padding; above 260px they keep their words whole (rule near body). */
@media (max-width:260px){
  :is(button,.btn,summary){overflow-wrap:anywhere}
  .btn{padding-inline:14px}
}
nav.gnb{flex:1;display:flex;justify-content:center}
nav.gnb>ul{list-style:none;margin:0;padding:0;display:flex;gap:2px}
nav.gnb>ul>li{position:relative}
nav.gnb .top{display:block;padding:9px 15px;border-radius:999px;font-weight:500;font-size:.95rem;text-decoration:none;white-space:nowrap}
nav.gnb .top:hover,nav.gnb li:focus-within>.top{background:var(--sunk)}
nav.gnb .top.on{background:var(--ink);color:var(--bg)}
/* Hover opens after a short intent delay and fades in from its trigger; keyboard focus
   opens it at once without animation (keyboard actions are never animated). */
nav.gnb .sub{position:absolute;left:50%;top:calc(100% + 14px);transform:translate(-50%,-4px);transform-origin:top center;min-width:220px;background:var(--surface);border:1px solid var(--line);border-radius:20px;padding:10px;list-style:none;margin:0;box-shadow:0 30px 60px -28px var(--shadow);display:block;visibility:hidden;opacity:0;transition:opacity 150ms var(--ease-out),transform 150ms var(--ease-out),visibility 0s linear 150ms}
nav.gnb li:hover>.sub{visibility:visible;opacity:1;transform:translate(-50%,0);transition:opacity 150ms var(--ease-out) 120ms,transform 150ms var(--ease-out) 120ms,visibility 0s linear 120ms}
nav.gnb li:focus-within>.sub{visibility:visible;opacity:1;transform:translate(-50%,0);transition:none}
/* APG disclosure navigation (RD-04), desktop only and only once site.js has upgraded the
   menu (nav[data-enhanced]): each section gets a toggle button after its link, and a sub-list
   leaves the tab order until that button opens it. Tab then visits 6 section links (+ their
   toggles) instead of every page. Hover still opens a sub-list; without JS, focus-within
   does as before. The toggle shows only while focused or open, so the bar keeps the
   prototype's look for pointer users; it hangs under the section name (never over it). */
nav.gnb .sub-toggle{display:none}
@media (min-width:1081px){
  nav.gnb .sub-toggle{position:absolute;z-index:1;left:50%;bottom:-14px;width:24px;height:24px;margin-left:-12px;padding:0;border:1px solid var(--line-strong);border-radius:50%;background:var(--surface);color:var(--ink);display:grid;place-items:center;cursor:pointer;opacity:0;pointer-events:none}
  nav.gnb .sub-toggle::before{content:"";width:6px;height:6px;border-right:1.5px solid currentColor;border-bottom:1.5px solid currentColor;transform:translateY(-1px) rotate(45deg)}
  nav.gnb .sub-toggle[aria-expanded="true"]::before{transform:translateY(1px) rotate(-135deg)}
  nav.gnb .sub-toggle:focus-visible,nav.gnb li.expanded>.sub-toggle{opacity:1;pointer-events:auto}
  nav.gnb[data-enhanced] li:focus-within:not(:hover):not(.expanded)>.sub{visibility:hidden;opacity:0;transition:none}
  nav.gnb[data-enhanced] li.expanded>.sub{visibility:visible;opacity:1;transform:translate(-50%,0);transition:none}
}
nav.gnb li.dismissed>.sub,nav.gnb[data-enhanced] li.dismissed>.sub{visibility:hidden;opacity:0;transition:none}
nav.gnb:focus{outline:none}
nav.gnb .sub::before{content:"";position:absolute;left:0;right:0;top:-16px;height:16px}
nav.gnb .sub a{display:flex;justify-content:space-between;padding:9px 14px;border-radius:12px;text-decoration:none;font-size:.95rem}
nav.gnb .sub a::after{content:"→";opacity:0;transition:opacity .15s}
nav.gnb .sub a:hover{background:var(--yellow-soft)}
nav.gnb .sub a:hover::after{opacity:1}
/* The account buttons may wrap onto a second line rather than push the page sideways
   (largest text plus WCAG 1.4.12 text spacing at 1280px overflowed by 42px). */
.auth{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:6px;min-width:0}
/* Member menu (partials/auth_links.html): a <details> disclosure, usable without JS. */
.member-menu{position:relative}
.member-menu>summary{list-style:none;cursor:pointer;user-select:none}
.member-menu>summary::-webkit-details-marker{display:none}
.member-menu .caret{width:.5em;height:.5em;border-right:1.5px solid currentColor;border-bottom:1.5px solid currentColor;transform:translateY(-2px) rotate(45deg);transition:transform .15s}
.member-menu[open] .caret{transform:translateY(2px) rotate(-135deg)}
.member-sub{position:absolute;right:0;top:calc(100% + 10px);z-index:30;min-width:184px;display:grid;gap:2px;padding:8px;background:var(--surface);color:var(--ink);border:1px solid var(--line);border-radius:18px;box-shadow:0 24px 48px -24px var(--shadow);animation:fade-in .15s ease-out}
.member-sub a,.member-sub button{display:flex;align-items:center;width:100%;min-height:var(--target-min);padding:8px 14px;border:0;border-radius:12px;background:none;color:inherit;font:inherit;font-size:.95rem;text-align:start;text-decoration:none;cursor:pointer}
.member-sub a:hover,.member-sub button:hover{background:var(--yellow-soft)}
.member-sub a[aria-current="page"]{font-weight:700;text-decoration:underline;text-decoration-color:var(--yellow-ink);text-underline-offset:4px}
.member-sub .sep{margin-top:4px;padding-top:4px;border-top:1px solid var(--line)}
.member-sub .who{overflow-wrap:anywhere;padding:6px 14px 8px;margin-bottom:2px;border-bottom:1px solid var(--line);font-size:.84rem;color:var(--muted);line-height:1.5}
@keyframes fade-in{from{opacity:0}to{opacity:1}}
@media (prefers-reduced-motion:reduce){.member-sub{animation:none}.member-menu .caret{transition:none}}
@media (forced-colors:active){.member-sub{border-color:CanvasText}}
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;padding:11px 22px;border-radius:999px;font-weight:500;text-decoration:none;border:1.5px solid var(--ink);background:transparent;cursor:pointer;line-height:1.3;transition:transform var(--dur-ui) var(--ease-out),background-color 150ms ease,border-color 150ms ease}
/* The display above beats the browser's own [hidden]{display:none}; a hidden button (JS-only
   지우기, 다시 보내기 before an error) stays hidden. Not !important: a page that keeps a hidden
   box's space on purpose (literature.css .tts) overrides it with a more specific rule. */
.btn[hidden]{display:none}
/* Hover lifts only where hover is real (no lift stuck after a tap); every press scales. */
@media (hover:hover) and (pointer:fine){
  .btn:hover{transform:translateY(-1px)}
  a.tab:not([aria-current]):hover,button.tab:not([aria-selected="true"]):hover{border-color:var(--ink)}
}
.btn:active,.tab:active{transform:scale(.97)}
.btn:disabled,.btn[aria-disabled="true"]{opacity:.55;cursor:not-allowed;transform:none}
/* Sending (site.js data-busy-label; CC-18): readable, and the pointer says "wait". */
.btn.is-busy{opacity:.8;cursor:progress}
.btn.fill{background:var(--yellow);border-color:var(--yellow);color:var(--on-yellow)}
.btn.dark{background:var(--ink);border-color:var(--ink);color:var(--bg)}
.btn.ghost{border-color:var(--line-strong)}
/* Destructive action (지우기, 탈퇴, 신청 취소): one definition; apps must not copy it. */
.btn.danger{background:var(--danger);border-color:var(--danger);color:var(--on-danger)}
.btn.light{border-color:var(--ink);color:var(--on-deep)}
/* On the --deep footer an --ink edge is 1.4:1; --on-deep-2 is the footer's own line colour (TZ-09). */
footer.site .btn.light{border-color:var(--on-deep-2)}
.btn.sm{padding:7px 15px;font-size:.9rem;min-height:var(--target-min)}
.menu-btn{display:none;align-items:center;justify-content:center;min-height:var(--target-min);min-width:var(--target-min);background:var(--ink);color:var(--bg);border:0;border-radius:999px;padding:9px 16px;cursor:pointer;font-weight:600;text-decoration:none;transition:transform var(--dur-ui) var(--ease-out)}
.menu-btn:active{transform:scale(.97)}

@media (max-width:1080px){
  /* One collapsed panel with or without JS: the "메뉴" link opens it with :target, and
     site.js upgrades the link to a button (.open). Search and account come first.
     Only an upgraded panel (nav[data-enhanced], set once site.js finished) is hidden by
     class: if the script fails in an old engine, :target still opens it (R4-XB-01).
     The panel ends 12px above the screen's bottom wherever it starts (--nav-top, measured
     by site.js; 140px ≈ bar + header without JS). 100vh first for engines without dvh
     (Safari < 15.4), so the panel always scrolls inside itself (R4-XB-04). */
  .menu-btn{display:inline-flex;margin-left:auto;flex-shrink:1;min-width:0;white-space:nowrap}
  .logo{flex-shrink:1;min-width:0}
  nav.gnb{display:none;position:absolute;z-index:5;left:clamp(16px,4vw,40px);right:clamp(16px,4vw,40px);top:calc(100% - 4px);background:var(--surface);border:1px solid var(--line);border-radius:24px;max-height:calc(100vh - var(--nav-top,140px) - 12px);max-height:calc(100dvh - var(--nav-top,140px) - 12px);overflow:auto;overscroll-behavior:contain;box-shadow:0 30px 60px -28px var(--shadow)}
  nav.gnb:target,nav.gnb.open{display:block}
  nav.gnb[data-enhanced]:not(.open){display:none}
  nav.gnb>ul{flex-direction:column;padding:8px 18px 18px}
  /* The desktop hover/focus rules (translate(-50%) under the section) outrank this one: on a
     tablet with a mouse or a zoomed desktop the hovered list jumped half its width left, out
     from under the pointer, so a click landed on nothing (found with RD-02). */
  nav.gnb li:is(:hover,:focus-within)>.sub{transform:none;transition:none}
  nav.gnb .sub{position:static;transform:none;visibility:visible;opacity:1;transition:none;display:grid;grid-template-columns:repeat(auto-fill,minmax(min(8em,100%),1fr));column-gap:8px;box-shadow:none;border:0;padding:0 0 6px;background:transparent;min-width:0}
  /* The desktop hover/focus shift (translate -50%) must not reach the phone panel: a tap
     on a sub link would land on the moved box and miss (baseline D1, RD-02). */
  nav.gnb li:hover>.sub,nav.gnb li:focus-within>.sub{transform:none}
  nav.gnb .sub a{min-height:var(--target-min);align-items:center;padding:8px 6px}
  nav.gnb .sub a::after{content:none}
  nav.gnb .sub::before{content:none}
  :root[data-font-scale$="xlarge"] nav.gnb .sub{grid-template-columns:1fr}
  nav.gnb .top{padding:12px 4px 2px;font-family:var(--serif);font-size:1.15rem;border-radius:0}
  nav.gnb .top:hover,nav.gnb li:focus-within>.top,nav.gnb .top.on{background:none;color:inherit}
  /* Where the reader is (ADR-0017 decision 13): the current page's link is marked in the
     open panel by weight and a band, not colour alone (WCAG 1.4.1). */
  nav.gnb .sub a[aria-current="page"],nav.gnb .sub a[aria-current="true"]{font-weight:700;background:var(--yellow-soft);border-radius:10px;text-decoration:underline;text-decoration-thickness:2px;text-underline-offset:5px}
  .auth{display:none}
  nav.gnb .m-search{display:block;padding:10px 4px 0}
  nav.gnb .m-search .hsearch input[type=search]{flex:1;width:100%;min-width:0}
  /* Largest text: the placeholder would be cut; the field gets the row, 검색 the next (R5-MT-09). */
  :root[data-font-scale$="xlarge"] nav.gnb .m-search .hsearch{flex-wrap:wrap}
  :root[data-font-scale$="xlarge"] nav.gnb .m-search .hsearch input[type=search]{flex-basis:100%}
  nav.gnb .m-auth{display:flex;flex-wrap:wrap;gap:8px;padding:10px 4px 12px;border-bottom:1px solid var(--line)}
  nav.gnb .m-close a{display:inline-flex;align-items:center;min-height:var(--target-min);margin-top:8px;font-weight:600}
  nav.gnb[data-enhanced] .m-close{display:none}
  /* More below (RD-03): while the panel can still scroll down (site.js data-fade, the
     .sub-nav logic), its bottom edge fades out. A sticky veil with a negative margin, so it
     never changes the panel's height and taps go through it. */
  nav.gnb[data-fade="end"]::after,nav.gnb[data-fade="both"]::after{content:"";display:block;position:sticky;bottom:0;height:56px;margin-top:-56px;background:linear-gradient(color-mix(in srgb,var(--surface) 0%,transparent),var(--surface) 85%);pointer-events:none}
}
@media (min-width:1081px){nav.gnb :is(.m-auth,.m-search,.m-close){display:none}}
/* Short screens (landscape phones, 200% zoom): the header scrolls away instead of
   covering half the screen, and the page head keeps less air (WCAG 1.4.10, 2.4.11). */
@media (max-height:560px){
  header.site{position:relative}
  /* The open menu panel grows to its full length and the page scrolls (it hangs from the
     header, which scrolls away here): a panel limited to the screen left ~70px of list at
     320×256, 400% zoom (RD-02). No inner scroll, so no fade either. */
  nav.gnb{max-height:none;overflow:visible}
  nav.gnb::after{content:none!important}
  .page-head .wrap{padding-block:24px 0}
  .page-head .head-end,.page-head .page-head-end{height:20px}
  .hero{min-height:0}
}

/* ── 히어로 ── */
.hero{position:relative;margin-top:calc(-1*var(--hh,82px));padding-top:var(--hh,82px);min-height:calc(100svh - var(--ah,36px));display:flex;flex-direction:column;background:var(--hero-bg);color:var(--on-deep);overflow:hidden;isolation:isolate}
.hero::after{content:"";position:absolute;inset:0;z-index:-1;opacity:.9;background:radial-gradient(60% 55% at 70% 56%,color-mix(in srgb,var(--bg) 78%,transparent),transparent 72%),radial-gradient(45% 60% at 20% 30%,var(--glow-1),transparent 65%),radial-gradient(40% 50% at 8% 95%,var(--glow-2),transparent 70%)}
.hero canvas{position:absolute;inset:0;width:100%;height:100%;z-index:-1;pointer-events:none}
.hero .wrap{flex:1;width:100%;display:flex;flex-direction:column;justify-content:center;padding-block:clamp(40px,6vh,80px)}
.hero .kick{display:flex;flex-wrap:wrap;gap:12px;align-items:center;margin-bottom:clamp(22px,3.5vh,40px)}
.hero .kick .dot{width:8px;height:8px;border-radius:50%;background:var(--yellow);box-shadow:0 0 0 6px rgba(247,199,25,.2)}
.hero .kick span{font-family:var(--mono);font-style:italic;font-size:clamp(1.05rem,1.4vw,1.3rem);letter-spacing:.06em;color:var(--on-deep-2)}
.hero h1{font-size:clamp(2.16rem,4.8vw,4.64rem);font-weight:500;letter-spacing:-.04em;line-height:1.14}
.hero h1 .thin{font-weight:300;font-style:normal;color:var(--ink-2)}
.hero h1 .y{color:var(--leaf);font-weight:600;background:linear-gradient(transparent 70%,var(--mark) 70%)}
.hero-grid{display:grid;grid-template-columns:minmax(0,.8fr) minmax(0,1.2fr);gap:clamp(20px,3vw,48px);align-items:center}
.hero-art{margin:0;align-self:end;justify-self:center;position:relative;order:-1}
.hero-art::before{content:"";position:absolute;left:8%;right:8%;bottom:-6px;height:22px;border-radius:50%;background:radial-gradient(closest-side,rgba(42,106,74,.22),transparent)}
.hero-art img{display:block;height:min(56vh,527px);width:auto;max-width:100%;object-fit:contain;transform-origin:50% 100%;animation:sway 7s ease-in-out infinite}
@keyframes sway{0%,100%{transform:rotate(-1.2deg)}50%{transform:rotate(1.2deg)}}
.hero .under{display:flex;flex-direction:column;gap:clamp(22px,3.5vh,34px);margin-top:clamp(24px,4vh,44px)}
.hero .lede{max-width:32em;color:color-mix(in srgb,var(--on-deep-2) 70%,var(--ink));font-size:clamp(1.08rem,1.5vw,1.3rem);font-weight:500;line-height:1.9}
.hero .org-name{display:flex;align-items:center;gap:14px;min-width:0;overflow-wrap:anywhere;margin-top:clamp(16px,2.4vh,26px);font-family:var(--serif);font-weight:600;font-size:clamp(1.15rem,1.7vw,1.5rem);letter-spacing:.02em;color:var(--leaf)}
.hero .org-name::before{content:"";width:36px;height:2px;background:var(--yellow);flex-shrink:0}
.hero .ctas{display:flex;flex-wrap:wrap;gap:12px}
.hero .ctas .btn{padding:15px 30px;font-size:1.08rem}
/* The outline CTA sits on the seed animation: a solid surface keeps its label readable. */
.hero .btn.light{background:var(--glass);-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px)}
/* "움직이는 그림 멈추기" (RD-09, WCAG 2.2.2): the seeds and the dandelion's sway move on their
   own, so a visible 44px control at the foot of the hero stops them. Without JS it posts the
   움직임 멈추기 setting; with JS it pauses them in place (.hero.is-paused). */
.hero .hero-foot{flex:none;flex-direction:row;justify-content:flex-end;padding-block:0 clamp(16px,3vh,28px)}
.hero-pause{margin:0}
.hero-pause button{display:inline-flex;align-items:center;gap:8px;min-height:var(--target-min);padding:0 16px;border:1.5px solid var(--line-strong);border-radius:999px;background:var(--surface);color:var(--ink);font-weight:600;font-size:.9rem;cursor:pointer;box-shadow:0 6px 18px -10px var(--shadow);transition:transform var(--dur-press) var(--ease-out),border-color var(--dur-ui) ease}
.hero-pause button:active{transform:scale(.97)}
@media (hover:hover) and (pointer:fine){.hero-pause button:hover{border-color:var(--ink)}}
.hero-pause .ico{width:10px;height:12px;flex:none;border-inline:3px solid currentColor}
.hero-pause button[aria-pressed="true"] .ico{width:0;height:0;border-inline:0;border-left:10px solid currentColor;border-block:6px solid transparent}
.hero.is-paused .hero-dandelion{animation-play-state:paused}
/* Nothing moves under reduced motion or 움직임 멈추기: the control has nothing to do. */
@media (prefers-reduced-motion:reduce){.hero-pause{display:none}}
:root[data-motion="off"] .hero-pause button[aria-pressed="false"]{display:none}
@media (max-width:640px){.hero-pause .wide{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}}
@media (forced-colors:active){.hero-pause button{border:1px solid ButtonText}}

/* ── 첫 화면 구역 (the cards under the hero: apps/core/static/core/home.css) ── */
section.block{padding-block:clamp(56px,8vw,100px)}
.head{display:flex;flex-wrap:wrap;gap:14px 28px;justify-content:space-between;align-items:end;margin-bottom:30px}
[data-route="home"] .head h2{font-size:clamp(2.1rem,3.8vw,3rem)}
/* Home sizes (prototype #main[data-route=home]; core/home.html sets the attribute). */
[data-route="home"] .work h3{font-size:1.5rem}
[data-route="home"] .work .ex{font-size:1.06rem}
.head h2{font-size:clamp(1.7rem,3vw,2.4rem);font-weight:500;letter-spacing:-.03em;line-height:1.25}
.head h2 .thin{font-weight:300}
.more{position:relative;font-weight:600;text-decoration:none;display:inline-flex;gap:8px;align-items:center;border-bottom:2px solid var(--yellow);padding-bottom:2px}
/* The underline is the box's own border, so the box keeps its height (prototype); a ::before
   grows only the hit area to --target-min, as the desktop view-setting pills do (R5-MT-05). */
.more::before{content:"";position:absolute;inset-inline:0;inset-block:calc((var(--target-min) - 100%) / -2)}
.more{transition:border-color var(--dur-ui) ease,color var(--dur-ui) ease}
@media (hover:hover) and (pointer:fine){.more:hover{border-bottom-color:var(--ink);color:var(--ink)}}
/* A card whose whole box is a link (association greeting's 영상 인사 보기). */
.tile{position:relative;border-radius:var(--r);padding:clamp(20px,2.4vw,30px);background:var(--surface);border:1px solid var(--line);overflow:hidden;text-decoration:none;display:flex;flex-direction:column;gap:12px;transition:transform 220ms var(--ease-out),box-shadow 220ms ease}

@media (max-width:980px){
  .hero-grid{grid-template-columns:minmax(0,1fr)} /* not 1fr: an auto minimum grows to the longest keep-all word (TZ-02) */
  .hero-art{justify-self:center;margin-top:8px;order:0}
  .hero-art img{height:min(39vh,289px)}
}

/* ── 작품 카드 ── */
.tabs{display:flex;flex-wrap:wrap;gap:6px}
.tab{border:1.5px solid var(--line-strong);background:var(--surface);border-radius:999px;padding:7px 17px;cursor:pointer;font-weight:600;font-size:.95rem;display:inline-flex;gap:8px;align-items:center;min-height:var(--target-min);transition:transform var(--dur-press) var(--ease-out),border-color 150ms ease}
.tab i{width:9px;height:9px;border-radius:50%;background:var(--gc,var(--ink))}
.tab[aria-selected="true"]{background:var(--ink);color:var(--bg);border-color:var(--ink)}
.works{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(270px,100%),1fr));gap:16px}
.work{--gc:var(--c-poem);position:relative;display:flex;flex-direction:column;gap:14px;padding:26px;background:var(--surface);border:1px solid var(--line);border-radius:var(--r);text-decoration:none;overflow:hidden}
.work::before{content:"";position:absolute;right:-60px;top:-60px;width:160px;height:160px;border-radius:50%;background:var(--gc);opacity:.35}
/* One hover cue (the lift), on real hover pointers only. The work card's
   hover, press and stretch link live in literature/literature.css (home and garden). */
@media (hover:hover) and (pointer:fine){
  a.tile:hover{transform:translateY(-4px);box-shadow:0 26px 50px -30px var(--shadow)}
}
.work .tags{display:flex;gap:6px;flex-wrap:wrap;font-size:.78rem;position:relative}
.chip{padding:3px 11px;border-radius:999px;background:var(--sunk);font-weight:600;display:inline-flex;align-items:center;gap:6px}
.chip i{width:8px;height:8px;border-radius:50%;background:var(--gc)}
.chip.award{background:var(--yellow-soft);color:var(--yellow-ink)}
.work h3{font-size:1.3rem;font-weight:500;letter-spacing:-.03em}
.work .ex{font-family:var(--read);color:var(--ink-2);white-space:pre-line;line-height:1.9;flex:1;position:relative}
.work .by{font-size:.86rem;color:var(--muted);display:flex;justify-content:space-between;gap:8px;border-top:1px solid var(--line);padding-top:12px;position:relative}
.work .by b{color:var(--ink);font-weight:600}

/* ── 서브 페이지 머리 ── */
.page-head{position:relative;margin-top:calc(-1*var(--hh,82px));padding-top:var(--hh,82px);background:var(--hero-bg);border-bottom:1px solid var(--line);color:var(--on-deep);overflow:hidden}
.page-head .wrap{position:relative;padding-block:clamp(44px,7vw,84px) 0}
.page-head .ghost-t{position:absolute;right:-.02em;bottom:-.22em;font-family:var(--mono);font-style:italic;font-weight:500;font-size:clamp(4.5rem,13vw,10rem);letter-spacing:-.02em;color:transparent;-webkit-text-stroke:1px var(--dl);line-height:1;pointer-events:none;white-space:nowrap}
.crumb{font-size:.84rem;color:var(--on-deep-2);display:flex;gap:8px;flex-wrap:wrap}
/* MP-09/R3-PAR-03: a 24px row in the layout (WCAG 2.5.8 AA; the prototype's height) whose
   padding/negative margin pair still gives a 44px hit area (--target-min) without growing it
   (content-box: the 24px is the row, the padding is added on top). */
/* Inline 8px too (R5-MT-06): neighbours never overlap, as the gap, "/" and its margin are wider
   than the two 8px extensions together. */
.crumb a{text-decoration:none;display:inline-flex;align-items:center;box-sizing:content-box;min-height:24px;padding:10px 8px;margin:-10px -8px}
/* A long current title wraps inside its crumb instead of widening the page (body keeps words
   whole). Only the current crumb may break anywhere: on every crumb, Firefox sized each one to
   its min-content and split "협회 / 안내" onto two lines (R4-XB-03). */
.crumb li{display:inline-flex;align-items:center;min-width:0;overflow-wrap:normal}
.crumb [aria-current]{overflow-wrap:anywhere;min-width:0}
.crumb ol{display:flex;gap:8px;flex-wrap:wrap}
.crumb li+li::before{content:"/";content:"/" / "";margin-right:8px;opacity:.6}
.crumb a:hover{color:var(--yellow-ink)}
.page-head h1{font-size:clamp(2rem,4vw,3.1rem);font-weight:500;letter-spacing:-.035em;margin-top:14px;line-height:1.02;position:relative}
.page-head .lead{margin-top:18px;color:var(--on-deep-2);max-width:40em;font-size:1.08rem;position:relative}
.page-head .head-action{margin-top:14px;position:relative}
/* No single word alone on a paragraph's last line where the engine can avoid it (VP4-19). */
:is(.page-head .lead,.help,.prose p,.acc .body p){text-wrap:pretty}
/* Sub-nav: one row of notch tabs. When it overflows (phones), site.js sets data-fade on
   the clipped side(s) and scrolls the current tab into view. aria-current is "page" on
   the page itself and "true" on its section tab (detail pages such as /photos/<pk>/). */
.sub-nav{display:flex;gap:4px;margin-top:36px;overflow-x:auto;overscroll-behavior-x:contain;position:relative;scrollbar-width:none}
.sub-nav::-webkit-scrollbar{display:none}
.sub-nav[data-fade="end"]{mask-image:linear-gradient(90deg,#000 85%,transparent)}
.sub-nav[data-fade="start"]{mask-image:linear-gradient(270deg,#000 85%,transparent)}
.sub-nav[data-fade="both"]{mask-image:linear-gradient(90deg,transparent,#000 15%,#000 85%,transparent)}
.sub-nav a{flex-shrink:0;padding:12px 18px;border-radius:16px 16px 0 0;text-decoration:none;font-size:.95rem;font-weight:600;white-space:nowrap;color:var(--on-deep-2);border:1px solid transparent;border-bottom:0}
.sub-nav a:hover{color:var(--on-deep)}
.sub-nav a[aria-current]{background:var(--bg);color:var(--ink);border-color:var(--line)}
/* Phones: the tabs wrap into rows of framed chips instead of a hidden-scrollbar strip (A-019,
   HM-34; TZ-12). The notch shape only works in one row, so every chip gets a full border;
   the current one an ink border. site.js has nothing to fade then. */
@media (max-width:640px){
  .sub-nav{flex-wrap:wrap;overflow:visible;mask-image:none;gap:8px;margin-top:28px;padding-bottom:20px}
  .sub-nav[data-fade]{mask-image:none}
  .sub-nav a{display:inline-flex;align-items:center;min-height:var(--target-min);padding:8px 14px;border:1px solid var(--line-strong);border-radius:12px;white-space:normal}
  .sub-nav a[aria-current]{border-color:var(--ink);box-shadow:inset 0 0 0 1px var(--ink)}
}
/* Compact head for long titles (detail pages): partials/page_head.html compact=True. */
.page-head--compact h1{font-size:clamp(1.6rem,3vw,2.2rem);line-height:1.2;text-wrap:balance}
.page-head--compact .ghost-t{opacity:.35}
.page{padding-block:clamp(40px,6vw,72px) 96px}

.prose{max-width:38em;font-family:var(--read);font-weight:var(--prose-weight);font-size:1.08rem;line-height:2.05}
.prose p+p{margin-top:1.1em}
.prose h3{font-family:var(--serif);font-size:1.5rem;margin:1.6em 0 .6em}
.sign{margin-top:2.4em;font-family:var(--serif)}
.sign small{display:block;color:var(--muted);font-family:var(--sans);font-size:.85rem}
.sign b{font-size:1.6rem;font-weight:700}

.two{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:clamp(28px,5vw,64px);align-items:start}
.two.w{grid-template-columns:minmax(0,1.4fr) minmax(0,1fr)}
.sec-t{font-size:clamp(1.4rem,2.4vw,1.8rem);font-weight:500;letter-spacing:-.03em;margin-bottom:22px}
.sec{margin-top:clamp(56px,8vw,96px)}

/* min(250px,100%): a fixed 250px minimum overflows a 195px window (390px phone at 200%,
   D17); the card then fills the row and its text wraps. */
.cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(250px,100%),1fr));gap:14px}
.card{background:var(--surface);border:1px solid var(--line);border-radius:20px;padding:24px;display:flex;flex-direction:column;gap:8px}
.card .num{font-family:var(--mono);font-size:.78rem;color:var(--muted);font-variant-numeric:lining-nums tabular-nums}
.card h3{font-size:1.25rem;font-weight:700}
.card p{color:var(--ink-2);font-size:1rem}
.card .ico{width:44px;height:44px;border-radius:14px;display:grid;place-items:center;background:var(--yellow-soft);color:var(--yellow-ink);margin-bottom:4px}
.card .ico svg{width:22px;height:22px}

.org{display:grid;gap:10px;justify-items:center;text-align:center}
.org .lv{display:flex;flex-wrap:wrap;justify-content:center;gap:10px}
.org .n{padding:12px 22px;border-radius:14px;background:var(--surface);border:1px solid var(--line);font-weight:600}
.org .n.top{background:var(--ink);color:var(--bg);border-color:var(--ink)}

.timeline{list-style:none;margin:0;padding:0;position:relative}
.timeline::before{content:"";position:absolute;left:118px;top:10px;bottom:10px;width:2px;background:var(--line)}
.timeline li{display:grid;grid-template-columns:100px 1fr;gap:40px;padding-block:22px;position:relative}
.timeline li::before{content:"";position:absolute;left:112px;top:34px;width:14px;height:14px;border-radius:50%;background:var(--yellow);border:3px solid var(--bg)}
.timeline .y{font-family:var(--mono);font-weight:600;font-size:2rem;letter-spacing:0;font-variant-numeric:lining-nums tabular-nums;line-height:1.2}
.timeline dl{margin:0;display:grid;grid-template-columns:auto 1fr;gap:6px 16px}
.timeline dt{font-family:var(--mono);font-size:.82rem;color:var(--muted);padding-top:3px;font-variant-numeric:lining-nums tabular-nums}
.timeline dd{margin:0}

/* --acc-d: the date column. A dated row's body hangs under its title (VP4-17); a row
   without a date (donations FAQ) keeps the full width. */
.acc{--acc-d:4.6rem;border-top:2px solid var(--ink)}
.acc details{border-bottom:1px solid var(--line)}
.acc summary{list-style:none;display:grid;grid-template-columns:var(--acc-d) 1fr auto auto;gap:18px;align-items:center;padding:20px 4px;cursor:pointer}
.acc summary::-webkit-details-marker{display:none}
.acc summary .d{font-family:var(--mono);font-size:.95rem;color:var(--muted);font-variant-numeric:lining-nums tabular-nums}
.acc summary .t{font-weight:600;font-size:1.05rem}
.acc summary .pm{width:30px;height:30px;border-radius:50%;background:var(--sunk);display:grid;place-items:center;transition:transform .2s}
.acc details[open] .pm{transform:rotate(45deg);background:var(--yellow);color:var(--on-yellow)}
.acc .body{padding:0 4px 24px;max-width:40em;color:var(--ink-2)}
@media (min-width:561px){.acc details:has(>summary>.d)>.body{padding-left:calc(4px + var(--acc-d) + 18px);max-width:calc(40em + var(--acc-d) + 22px)}}
.acc .body p+p{margin-top:.7em}
.cat{font-size:.78rem;font-weight:700;padding:3px 10px;border-radius:999px;background:var(--leaf-soft);color:var(--leaf);white-space:nowrap}

.toolbar{display:flex;flex-wrap:wrap;gap:12px 20px;align-items:center;justify-content:space-between;margin-bottom:28px}
.field{display:flex;flex-direction:column;gap:6px}
/* Flex items default to min-width:auto (their content's width); a toolbar item or a field part
   alone on its line may then be wider than the window (issue picker, month picker at 195 CSS px,
   D17). min-width:0 lets it shrink to the line; its control stops at max-width:100%. */
:is(.toolbar,.field)>*{min-width:0}
/* A row of buttons/links under a form or panel (accounts, community, donations, magazine). */
.form-actions,.cta-row{display:flex;flex-wrap:wrap;gap:10px;align-items:center}
.field label,.field legend{font-weight:600;font-size:.92rem}
input[type=text],input[type=email],input[type=password],input[type=tel],input[type=number],input[type=search],select,textarea{width:100%;padding:13px 16px;border:1.5px solid var(--line-strong);border-radius:14px;background:var(--surface)}
/* Form controls get the same two-tone --focus ring as everything else (TZ-01, KS-06); the
   ink border is an extra cue. Text fields always match :focus-visible, so nothing is lost. */
/* Placeholders are text (WCAG 1.4.3): --muted, ≥4.5:1 on --surface
   (tests/test_theme_tokens.py); opacity:1 undoes Firefox's faded default (PAR-12, MA-11). */
::placeholder{color:var(--muted);opacity:1}
input:focus-visible,select:focus-visible,textarea:focus-visible{border-color:var(--ink);outline:3px solid var(--focus);outline-offset:2px;box-shadow:0 0 0 2px var(--bg)}
select{color:var(--ink);background-color:var(--surface)}
textarea{min-height:180px;resize:vertical;line-height:1.8}
select.inline{width:auto;min-height:var(--target-min);padding:9px 38px 9px 16px;border-radius:999px}
/* A field is never wider than its box. A percentage max-width also makes the browser count
   the control as shrinkable (CSS Sizing 3 "compressible"), so a select sized by its longest
   option ("2026년 9월호") no longer sets a row's minimum width at 195 CSS px (D17). */
:is(input,select,textarea){max-width:100%}
form.form{display:grid;gap:18px;background:var(--surface);border:1px solid var(--line);border-radius:var(--r);padding:clamp(22px,4vw,36px)}
/* Phones: a card's frame keeps its look but gives its words the width (a 320px screen
   kept ~240px of line inside a padded card; ADR-0017 decision 13). */
@media (max-width:480px){form.form{padding:20px 16px;border-radius:20px}.card,.box{padding:18px}}
form.form h3{font-size:1.4rem}
.row2{display:grid;grid-template-columns:1fr 1fr;gap:14px}
/* min-inline-size:0 — a fieldset is min-content wide by default, so one long unbroken word
   (an e-mail address) inside it would push past the card at 320px (R5-SR-01). */
fieldset{border:0;padding:0;margin:0;min-inline-size:0}
/* Every pointer gets the 44px row and the 22px box (the site's --target-min floor, D15;
   PAR-09): a mouse with a tremor misses a 26px row as easily as a finger. */
.check{display:flex;gap:10px;align-items:center;min-height:var(--target-min);font-size:.96rem;cursor:pointer}
.check > span{min-width:0;overflow-wrap:anywhere}
.check input{width:22px;height:22px;margin:0;accent-color:var(--leaf);flex-shrink:0}
.done{padding:14px 18px;border-radius:14px;background:var(--leaf-soft);color:var(--leaf);font-weight:600}
.drop{border:2px dashed var(--line);border-radius:16px;padding:22px;text-align:center;color:var(--muted);font-size:.95rem}
.drop input{margin-top:10px;width:100%;max-width:100%}

/* Option cards (magazine formats, donation choices). A drawn check box shows the state
   and in forced colours, never colour alone (WCAG 1.4.1). */
.pick{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px}
.pick label{position:relative;cursor:pointer}
.pick input{position:absolute;opacity:0}
.pick .o{position:relative;display:flex;flex-direction:column;gap:2px;padding:16px 44px 16px 16px;border:1.5px solid var(--line-strong);border-radius:16px;background:var(--surface);height:100%;transition:border-color 150ms ease,background-color 150ms ease,transform var(--dur-press) var(--ease-out)}
.pick .o::before{content:"";position:absolute;top:16px;right:14px;width:20px;height:20px;border-radius:6px;border:1.5px solid var(--line-strong);background:var(--surface);color:var(--bg);display:grid;place-items:center;font-size:.8rem;font-weight:700;line-height:1}
.pick .o b{font-size:1.05rem}
.pick .o small{color:var(--muted);font-size:.84rem}
.pick input:checked+.o{border-color:var(--ink);box-shadow:inset 0 0 0 1.5px var(--ink);background:var(--yellow-soft)}
.pick input:checked+.o::before{content:"✓";content:"✓" / "";background:var(--ink);border-color:var(--ink)}
.has-error .pick .o{border-color:var(--ink)}
.pick input:focus-visible+.o{outline:3px solid var(--focus);outline-offset:2px}
@media (hover:hover) and (pointer:fine){.pick label:hover .o{border-color:var(--ink-2)}}
.pick label:active .o{transform:scale(.98)}
/* One choice (radio) reads as a ring with a dot, many (checkbox) as a ticked square (MA-12). */
.pick input[type=radio]+.o::before{border-radius:50%}
.pick input[type=radio]:checked+.o::before{content:"";content:"" / "";background:var(--surface);box-shadow:inset 0 0 0 5px var(--ink)}
.amounts{display:flex;flex-wrap:wrap;gap:8px}
.amounts label{position:relative}
.amounts input{position:absolute;opacity:0}
.amounts span{display:block;padding:10px 18px;border-radius:999px;border:1.5px solid var(--line-strong);cursor:pointer;font-weight:600;font-variant-numeric:tabular-nums;background:var(--surface)}
.amounts input:checked+span{background:var(--ink);color:var(--bg);border-color:var(--ink)}
.amounts input:checked+span::before{content:"✓";content:"✓" / "";margin-right:6px}
.amounts input:focus-visible+span{outline:3px solid var(--focus);outline-offset:2px}
.seg{display:inline-flex;background:var(--sunk);border-radius:999px;padding:4px}
.seg label{position:relative}
.seg input{position:absolute;opacity:0}
.seg span{display:block;padding:8px 20px;border-radius:999px;font-weight:600;cursor:pointer}
/* Selected: the prototype's raised pill plus an ink ring and a ✓, so the choice never
   rests on the faint surface/sunk difference. */
.seg input:checked+span{background:var(--surface);box-shadow:inset 0 0 0 2px var(--ink),0 2px 8px -3px var(--shadow)}
.seg input:checked+span::before{content:"✓";content:"✓" / "";margin-right:6px}
.seg input:focus-visible+span{outline:3px solid var(--focus)}

/* ── 작품 읽기 ── */
.progress{position:fixed;left:0;top:0;height:4px;background:var(--yellow);width:0;z-index:100}
.reader{display:grid;grid-template-columns:minmax(0,1fr) 280px;gap:56px}
.reader .body{font-family:var(--read);font-weight:var(--prose-weight);font-size:1.15rem;line-height:2.15;white-space:pre-line;max-width:34em}
.reader aside{display:grid;gap:14px;align-content:start} /* literature.css: only the first visible box is sticky */
.box{background:var(--surface);border:1px solid var(--line);border-radius:20px;padding:20px;display:grid;gap:10px}
.box dl{margin:0;display:grid;grid-template-columns:auto minmax(0,1fr);gap:6px 14px;font-size:1rem}
.box dt{color:var(--muted)}
.box dd{margin:0;font-weight:600;min-width:0;overflow-wrap:anywhere}
/* A phone number, address or map link in a box is a whole 44 px row to tap (call, mail). */
.box dd>a{display:inline-flex;align-items:center;min-height:var(--target-min)}
/* Phones: label over value, so a long value (an e-mail address) keeps the line's width
   instead of breaking every few letters beside its label. */
@media (max-width:420px){.box dl{grid-template-columns:minmax(0,1fr);row-gap:0}.box dd+dt{margin-top:10px}}
.comments{margin-top:56px;max-width:40em}
.cmt{display:grid;grid-template-columns:40px 1fr;gap:12px;padding:16px 0;border-top:1px solid var(--line)}
.av{width:40px;height:40px;border-radius:50%;display:grid;place-items:center;font-weight:700;background:var(--gc,var(--sunk));color:#121C16}

/* ── 월간지 ── */
.cover{aspect-ratio:3/4;max-width:100%;border-radius:18px;padding:22px;display:flex;flex-direction:column;justify-content:space-between;color:#121C16;position:relative;overflow:hidden;box-shadow:0 30px 60px -34px var(--shadow)}
.cover .vol{font-family:var(--mono);font-style:italic;font-weight:600;font-size:.9rem;letter-spacing:.06em;font-variant-numeric:lining-nums}
.cover h3{font-size:clamp(1.6rem,2.4vw,2.1rem);font-weight:600;letter-spacing:-.03em;line-height:1;position:relative}
.cover p{font-size:.88rem;position:relative;margin-top:8px}
.cover svg{position:absolute;right:-26px;top:50px;width:62%;opacity:.7}
.monthly{display:grid;grid-template-columns:minmax(0,340px) minmax(0,1fr);gap:clamp(28px,5vw,60px);align-items:start}
.edit-note{padding:22px 24px;border-radius:20px;background:var(--sunk);font-family:var(--read);line-height:1.9;margin-bottom:24px}
.edit-note b{font-family:var(--sans);font-size:.8rem;color:var(--muted);display:block;margin-bottom:6px;letter-spacing:.06em}
.toc{list-style:none;margin:0;padding:0;border-top:2px solid var(--ink)}
.toc li{border-bottom:1px solid var(--line)}
.toc a,.toc .r{display:grid;grid-template-columns:3.6em 1fr auto;gap:14px;padding:15px 4px;align-items:baseline;text-decoration:none}
.toc a:hover .tt{text-decoration:underline;text-decoration-color:var(--yellow);text-decoration-thickness:3px}
.toc .p{font-family:var(--mono);font-size:.82rem;color:var(--muted);font-variant-numeric:lining-nums tabular-nums}
.toc .tt{font-weight:600}
.toc .g{font-size:.78rem;color:var(--leaf);font-weight:700}
.shelf{display:grid;grid-template-columns:repeat(auto-fill,minmax(120px,1fr));gap:14px}
.shelf .cover{padding:12px;border-radius:12px;box-shadow:none}
.shelf .cover h3{font-size:1.05rem}
.shelf .cover .vol{font-size:.6rem}
.shelf span{display:block;font-size:.86rem;margin-top:8px;color:var(--muted)}
.plans{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:14px}
.plan{border-radius:20px;padding:22px;background:var(--surface);border:1px solid var(--line);display:flex;flex-direction:column;gap:8px}
.plan .price{font-family:var(--serif);font-weight:500;font-size:1.7rem;letter-spacing:-.03em}
.plan ul{margin:0;padding-left:1.1em;color:var(--ink-2);font-size:.93rem}
.plan.hl{background:var(--yellow);border-color:var(--yellow);color:var(--on-yellow)}
.plan.hl ul{color:inherit}

/* ── 게시판 ── */
.board{width:100%;border-collapse:collapse}
.board th{text-align:left;font-weight:600;font-size:.84rem;color:var(--muted);padding:12px 10px;border-bottom:2px solid var(--ink)}
.board td{padding:15px 10px;border-bottom:1px solid var(--line);vertical-align:middle}
.board td.n{font-family:var(--mono);font-size:.84rem;color:var(--muted);white-space:nowrap;font-variant-numeric:lining-nums tabular-nums}
.board td.r{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap}
.board button.lk{background:none;border:0;padding:0;cursor:pointer;text-align:left;font-weight:600}
.board button.lk:hover{text-decoration:underline;text-decoration-color:var(--yellow);text-decoration-thickness:3px}
.board tr.open td{background:var(--sunk)}
.board .post{padding:6px 4px 12px;color:var(--ink-2);line-height:1.9;white-space:pre-line}
.scroll{overflow-x:auto;position:relative} /* contains absolutely positioned .visually-hidden text */
.pin td{background:var(--yellow-soft)}
.search{display:flex;gap:8px;max-width:360px;flex:1;align-items:center}
/* A pill like the header field (.hsearch) beside the round button (VP4-06). */
.search input[type=search]{min-height:var(--target-min);padding-block:9px;border-radius:999px}

.gallery{columns:3 260px;column-gap:16px}
.gallery figure{margin:0 0 16px;break-inside:avoid}
.ph{border-radius:18px;position:relative;overflow:hidden}
.ph svg{display:block;width:100%;height:auto}
.gallery figcaption{margin-top:10px;font-size:.95rem;font-weight:600}
.gallery figcaption small{display:block;color:var(--muted);font-weight:400}
.play{position:absolute;inset:0;display:grid;place-items:center}
.play i{width:62px;height:62px;border-radius:50%;background:rgba(255,255,255,.92);display:grid;place-items:center}
.play i::after{content:"";border-left:17px solid #121C16;border-block:11px solid transparent;margin-left:5px}
.dur{position:absolute;right:12px;bottom:12px;background:rgba(0,0,0,.88);color:#fff;font-family:var(--sans);font-size:.76rem;padding:2px 9px;border-radius:8px}
.badges{display:flex;gap:6px;flex-wrap:wrap;margin-top:6px}
.badges span{font-size:.72rem;font-weight:700;padding:2px 8px;border-radius:6px;background:var(--leaf-soft);color:var(--leaf)}
dialog{border:0;padding:0;border-radius:24px;max-width:min(900px,calc(100vw - 32px));width:100%;background:var(--surface);color:var(--ink)}
dialog::backdrop{background:rgba(5,12,8,.8);backdrop-filter:blur(6px)}
dialog .dx{padding:20px 24px;display:flex;justify-content:space-between;gap:16px;align-items:start}
dialog .ph{border-radius:0}

/* ── 후원 ── */
.impact{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:14px}
.impact div{border-radius:20px;padding:22px;background:var(--surface);border:1px solid var(--line)}
.impact b{display:block;font-family:var(--serif);font-weight:500;font-size:1.6rem;letter-spacing:-.03em}
.impact p{color:var(--ink-2);font-size:.95rem;margin-top:4px}
.bigmeter{border-radius:var(--r);padding:clamp(24px,4vw,36px);background:var(--soft-bg);border:1px solid var(--line);color:var(--on-deep);display:grid;gap:14px}
.bigmeter .pct{font-family:var(--mono);font-weight:600;font-size:clamp(2.8rem,6vw,4.2rem);line-height:1;color:var(--leaf);font-variant-numeric:lining-nums tabular-nums}
.bigmeter .meter{background:var(--sunk)}
.bigmeter .meter{height:14px}
.bigmeter .row{display:flex;justify-content:space-between;flex-wrap:wrap;gap:8px;color:var(--on-deep-2);font-size:.92rem;font-variant-numeric:tabular-nums}
.plot{position:relative;border-radius:var(--r);overflow:hidden}
.plot svg{display:block;width:100%;height:auto}
.steps{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:14px;counter-reset:s}
.steps li{counter-increment:s;padding:24px;border-radius:20px;background:var(--surface);border:1px solid var(--line)}
.steps li::before{content:counter(s,decimal-leading-zero);font-family:var(--mono);font-size:.8rem;color:var(--muted);display:block;margin-bottom:10px;font-variant-numeric:lining-nums tabular-nums}
.steps b{display:block;font-size:1.12rem;margin-bottom:4px}
.steps p{color:var(--ink-2);font-size:1rem}
.bars{display:grid;gap:12px}
.bars .b{display:grid;grid-template-columns:3.2em 1fr 8.4em;gap:14px;align-items:center;font-size:.92rem}
.bars .b .tr{height:26px;border-radius:8px;background:var(--sunk);overflow:hidden}
.bars .b .tr i{display:block;height:100%;background:var(--leaf);border-radius:8px}
.bars .b.cur .tr i{background:var(--yellow)}
.bars .b span:last-child{text-align:right;font-variant-numeric:tabular-nums;font-weight:600}
.donut{display:grid;grid-template-columns:180px 1fr;gap:26px;align-items:center}
.legend{list-style:none;margin:0;padding:0;display:grid;gap:10px}
.legend li{display:grid;grid-template-columns:12px 1fr auto;gap:10px;align-items:center;font-size:.95rem}
.legend i{width:12px;height:12px;border-radius:4px}
.legend span:last-child{font-variant-numeric:tabular-nums;font-weight:600}

/* ── 로그인 ── */
.auth-wrap{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:0;border-radius:28px;overflow:hidden;border:1px solid var(--line);background:var(--surface)}
.auth-side{background:var(--soft-bg);color:var(--on-deep);padding:clamp(28px,5vw,52px);display:flex;flex-direction:column;justify-content:space-between;gap:30px;position:relative;overflow:hidden;min-height:420px}
.auth-side h2{font-size:clamp(1.7rem,3vw,2.3rem);font-weight:500;letter-spacing:-.03em;line-height:1.3}
.auth-side svg{position:absolute;right:-40px;bottom:-40px;width:260px;opacity:.25}
.auth-wrap form.form{border:0;border-radius:0}
.social{display:grid;gap:8px}
.social button{padding:12px;border-radius:14px;border:1.5px solid var(--line);background:var(--surface);font-weight:600;cursor:pointer}
.or{display:flex;align-items:center;gap:12px;color:var(--muted);font-size:.85rem}
.or::before,.or::after{content:"";flex:1;height:1px;background:var(--line)}
.stepper{display:flex;gap:6px;margin-bottom:6px}
.stepper span{flex:1;height:5px;border-radius:999px;background:var(--line)}
.stepper span.on{background:var(--yellow)}

footer.site{background:var(--deep);color:var(--on-deep);padding-block:64px 36px;border-top:1px solid var(--line)}
footer .big{font-family:var(--serif);font-weight:500;font-size:clamp(2.4rem,6vw,4.4rem);letter-spacing:-.04em;line-height:1;color:var(--on-deep-accent);margin-bottom:40px}
footer .cols{display:grid;grid-template-columns:1.4fr repeat(5,1fr);gap:24px}
footer .cols>*{min-width:0}
footer h4,footer .foot-h{margin:0 0 12px;font-size:.92rem;font-family:var(--serif);font-weight:500}
footer ul{list-style:none;margin:0;padding:0;display:grid;gap:5px;font-size:.88rem;color:var(--on-deep-2)}
footer a{text-decoration:none}
/* One-syllable genre links (시, 소설) are 44px wide at least; every row is 24px tall at
   least on wide screens too (WCAG 2.5.8; RD-23, MA-22). */
footer ul a{display:inline-flex;align-items:center;min-width:var(--target-min);min-height:24px}
footer .org-info a{display:inline-flex;align-items:center;min-height:24px}
footer a:hover{color:var(--on-deep-accent)}
/* Tablets: the 5px rhythm stays, but each link is a 32px row (tremor persona; MP-09). */
@media (max-width:980px){
  footer ul{gap:0}
  footer ul a{display:inline-flex;align-items:center;min-height:32px}
}
/* Phones (ADR-0017 decision 13): every footer link is a 44px target, and a section's
   links run along the line (one section per row) instead of down a narrow column, so
   the bigger targets hardly lengthen the footer. */
@media (max-width:640px){
  footer.site .cols{grid-template-columns:minmax(0,1fr);gap:20px} /* outranks the tablet rule below */
  footer ul{display:flex;flex-wrap:wrap;column-gap:14px;font-size:.95rem}
  footer ul a{min-height:var(--target-min)}
  footer .foot-h{margin-bottom:2px;font-size:1rem}
}
footer .fine{margin-top:44px;padding-top:22px;border-top:1px solid var(--dl);font-size:.8rem;color:var(--on-deep-2);display:flex;flex-wrap:wrap;justify-content:space-between;gap:10px}

@media (max-width:980px){
  .two,.two.w,.monthly,.reader,.auth-wrap{grid-template-columns:minmax(0,1fr)}
  .plans{grid-template-columns:repeat(2,minmax(0,1fr))}
  .impact,.steps{grid-template-columns:1fr}
  /* Two columns while a 7rem column fits, one below that (a 390px phone at 200% with large
     text is 195 CSS px, D17); minmax(min(..,100%)) so no column outgrows the window. */
  footer .cols{grid-template-columns:repeat(auto-fit,minmax(min(100%,max(7rem,calc(50% - 12px))),1fr))}
  .auth-side{min-height:0}
}
@media (max-width:560px){
  .row2,.plans{grid-template-columns:1fr}
  .timeline li{grid-template-columns:1fr;gap:6px}
  .acc summary{grid-template-columns:1fr auto;gap:6px 12px}
  .acc summary .d{grid-column:1/-1}
  .acc summary .cat{display:none}
  .donut{grid-template-columns:minmax(0,1fr);justify-items:center}
  .donut svg{max-width:100%;height:auto}
  .bars .b{grid-template-columns:2.8em 1fr 7em;gap:8px}
  .board .hide-s{display:none}
}
/* Narrow window (195 CSS px at 200% zoom, D17; after the 980px rule): month and amount share the first line, the
   bar takes the full width under them — the three columns needed ~200px at 130% text. */
@media (max-width:260px){
  .bars .b{grid-template-columns:minmax(0,1fr) auto;row-gap:4px}
  .bars .b .tr{grid-column:1/-1;grid-row:2}
}
/* Reduced motion removes movement (animations, transform and size transitions, smooth
   scrolling) but keeps short fades and colour changes: an instant swap of a whole menu or a
   hover colour is harsher than a 160ms fade, and nothing moves (VP4-18, WCAG 2.3.3). */
@media (prefers-reduced-motion:reduce){*,*::before,*::after{animation:none!important;scroll-behavior:auto!important;transition-property:opacity,color,background-color,border-color,outline-color,visibility!important}}
/* "움직임 멈추기" view setting (apps/core/preferences.py Motion; WCAG 2.2.2). */
:root[data-motion="off"]{scroll-behavior:auto}
@media (prefers-reduced-motion:reduce){:is(.btn:hover,.btn:active,.tab:active,.pick label:active .o,a.tile:hover){transform:none}}
:root[data-motion="off"] *,:root[data-motion="off"] *::before,:root[data-motion="off"] *::after{animation:none!important;transition-property:opacity,color,background-color,border-color,outline-color,visibility!important}
:root[data-motion="off"] :is(.btn:hover,.btn:active,.tab:active,.pick label:active .o,a.tile:hover){transform:none}

/* ── Project additions (server-rendered layout; replaces the prototype's inline styles) ── */
.footer-lead{color:var(--on-deep-2);font-size:.92rem;max-width:18em}
.footer-ctas{display:flex;gap:8px;margin-top:16px;flex-wrap:wrap}
.inline-form{display:inline}
/* Messages (partials/messages.html): a band under the floating header that pushes the
   hero/page head down, so nothing paints over it (those normally slide under the header). */
.messages{position:relative;z-index:1;margin-top:calc(-1*var(--hh,82px));padding-top:calc(var(--hh,82px) + 8px);padding-bottom:4px;background:var(--hero-bg)}
/* Two rules: an engine without :has() drops a whole selector list that contains it (R4-XB-15). */
.messages+:is(.hero,.page-head){margin-top:0;padding-top:0}
main:has(>.messages:first-child) :is(.hero,.page-head){margin-top:0;padding-top:0}
/* Under a page head (partials/page_head.html): plain page background, first thing after the h1. */
.page-head+.messages{margin-top:0;padding-top:20px;padding-bottom:0;background:none}
.page-head+.messages+.page{padding-top:clamp(24px,4vw,48px)}
.msg-list{display:grid;gap:8px}
.msg-list:empty{display:none}
.msg-list:focus{outline:none}
.msg-list:focus-visible{outline:3px solid var(--focus);outline-offset:4px;border-radius:16px}
.msg{display:flex;gap:10px;align-items:flex-start;padding:12px 16px;border-radius:14px;font-weight:600;border:1px solid transparent}
.msg-mark{flex:none;display:inline-grid;place-items:center;width:1.5em;height:1.5em;margin-top:.12em;border-radius:50%;font-size:.86em;line-height:1;font-weight:700}
.msg-success{background:var(--leaf-soft);color:var(--leaf);border-color:color-mix(in srgb,var(--leaf) 35%,transparent)}
.msg-success .msg-mark{background:var(--leaf);color:var(--on-leaf)}
.msg-info,.msg-debug{background:var(--sunk);color:var(--ink);border-color:var(--ink)}
.msg-info .msg-mark,.msg-debug .msg-mark{border:1.5px solid currentColor;font-family:var(--serif);font-style:italic}
.msg-warning,.msg-error{background:var(--yellow-soft);color:var(--ink);border:2px solid var(--ink)}
.msg-warning .msg-mark,.msg-error .msg-mark{background:var(--ink);color:var(--bg)}
/* A message's own link (apps.core.messages): inherits the message colour, always underlined. */
.msg-link{color:inherit;text-decoration:underline;text-underline-offset:3px}
.visually-hidden{position:absolute!important;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}

/* ── Forced colours beyond the prototype ── */
/* Windows High Contrast / forced colors: keep boundaries, states and focus visible with system colors. */
@media (forced-colors: active){
  mark{forced-color-adjust:none;background:Mark;color:MarkText}
  .btn,.tab,.tile,.card,.box,.plan,.chip,.bar,.a11y button,.menu-btn,nav.gnb .sub,input,select,textarea{border:1px solid CanvasText}
  /* Current/pressed states: ":root …" outranks the component rules (.sub-nav a[aria-current],
     .tab[aria-current="page"]) that would otherwise keep their own surface (TZ-08). */
  :root :is(.sub-nav a[aria-current],.tab[aria-current],.a11y button[aria-pressed="true"],.tab[aria-selected="true"],nav.gnb .top.on,[aria-current="page"]){forced-color-adjust:none;background:Highlight;color:HighlightText;border-color:Highlight}
  :focus-visible,:is(input,select,textarea):focus-visible{outline:3px solid Highlight}
  .skip{forced-color-adjust:none;background:Highlight;color:HighlightText}
  svg{forced-color-adjust:auto}
  progress{border:1px solid CanvasText}
  /* Selected segment / card: the system highlight (surface colours are dropped). */
  .seg{border:1px solid CanvasText;background:Canvas}
  .amounts span,.pick .o{border:1px solid CanvasText}
  .pick .o::before{border-color:CanvasText}
  :root .seg button[aria-pressed="true"],:root .seg input:checked+span,:root .amounts input:checked+span,:root .pick input:checked+.o{forced-color-adjust:none;background:Highlight;color:HighlightText;border-color:Highlight}
  :root .pick input:checked+.o small{color:HighlightText}
  :root .pick input:checked+.o::before{background:HighlightText;color:Highlight;border-color:HighlightText}
  :root .pick input[type=radio]:checked+.o::before{background:Highlight;box-shadow:inset 0 0 0 5px HighlightText}
  .sub-nav{mask-image:none}
  nav.gnb::after{content:none!important}
  nav.gnb .sub-toggle{border-color:ButtonText}
  .ex-tag,.btn.danger{border:1px solid CanvasText}
  .msg{border:2px solid CanvasText}
  /* The outlined ghost word would turn into solid giant text; the brand mark keeps its colours. */
  .page-head .ghost-t{display:none}
  .logo svg{forced-color-adjust:none}
  .play i{forced-color-adjust:none;background:ButtonText}
  .play i::after{border-left-color:ButtonFace}
}

/* ── Home (apps/core/templates/core/home.html) — platform integration additions ── */
/* Hero illustration (core/_dandelion.svg): the prototype's full-colour dandelion drawn with
   tokens only. Same box as the
   prototype image (height-led, 756×1004 ratio). */
.hero-art{max-width:100%}
.hero-dandelion{display:block;height:min(56vh,527px);width:auto;max-width:100%;aspect-ratio:378/502;transform-origin:50% 100%;animation:sway 7s ease-in-out infinite;
  /* Leaf and flower colours are illustration tokens (tokens.css --art-*), not the text green
     or UI yellow: the prototype's bright leaves and lemon flowers. */
  --art-stem:color-mix(in srgb,var(--art-leaf) 40%,var(--surface));
  --art-vein:color-mix(in srgb,var(--art-leaf) 30%,var(--surface));
  --art-ray:color-mix(in srgb,var(--ink) 20%,var(--surface));
  --art-core:color-mix(in srgb,var(--art-leaf) 45%,var(--surface))}
@media (max-width:980px){.hero-dandelion{height:min(39vh,289px)}}
/* Phones: a smaller picture, so the first cards start within a short scroll of the
   calls to action (ADR-0017 decision 13). */
@media (max-width:640px){.hero-dandelion{height:min(30vh,230px)}}
.hero-dandelion .leaf{fill:var(--art-leaf);stroke:var(--surface)}
.hero-dandelion .leaf.b{fill:var(--art-leaf-2)}
.hero-dandelion .vein{stroke:var(--art-vein);fill:none}
.hero-dandelion .stem{stroke:var(--art-stem);fill:none}
.hero-dandelion .rays,.hero-dandelion .puff{stroke:var(--art-ray);fill:none}
.hero-dandelion .tips{fill:var(--art-ray)}
.hero-dandelion .hub{fill:var(--surface)}
.hero-dandelion .ring{fill:var(--art-core)}
.hero-dandelion .flower{fill:var(--art-flower)}
.hero-dandelion .flower-in{fill:var(--art-flower-in)}
.hero-dandelion .centre{fill:var(--surface)}
.garden-more{margin-top:30px}

/* ── Shared components (single definition; apps must not copy these) ── */
.plain-list{list-style:none;margin:0;padding:0}
a,button{touch-action:manipulation}
a.tab{text-decoration:none;color:inherit}
.tab[aria-current="page"]{background:var(--ink);color:var(--bg);border-color:var(--ink)}
.page-head .head-end{height:44px}
.page-head .sub-nav+.head-end{display:none}
/* Pager (templates/partials/pagination.html): KRDS — the current page is filled and underlined, not colour only. */
.pager{margin-top:28px;display:grid;justify-items:center;gap:8px}
.pager ul{display:flex;flex-wrap:wrap;gap:8px;justify-content:center}
.pager a,.pager .gap{display:inline-flex;min-width:var(--target-min);min-height:var(--target-min);align-items:center;justify-content:center;padding:0 12px;border-radius:999px;text-decoration:none;font-weight:600;font-variant-numeric:tabular-nums}
.pager a{border:1.5px solid var(--line)}
.pager a[href]:hover{border-color:var(--ink)}
.pager a[aria-current="page"]{background:var(--ink);color:var(--bg);border-color:var(--ink);text-decoration:underline;text-underline-offset:4px}
.pager .pager-count{font-variant-numeric:tabular-nums}

/* ── Header search (partials/search_form.html; D15) ──
   Desktop: a 44px "검색" control (a link to /search/ without JS). site.js makes it a
   disclosure button that shows the field in the nav's place, so the header keeps its
   height and the centred menu its prototype position. Phones use the menu panel's field. */
.hsearch{display:flex;gap:6px;align-items:center}
/* 16px at least: iOS zooms the page into any field below 16px on focus (R4-XB-09). */
.hsearch input[type=search]{width:11em;padding:6px 14px;border-radius:999px;font-size:max(1rem,16px);min-height:var(--target-min)}
.h-search-btn{flex-shrink:0;gap:6px}
.h-search-btn .ico{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round}
.h-search{display:none}
.bar.searching nav.gnb{display:none}
.bar.searching .h-search:not([hidden]){display:flex;flex:1;min-width:0;justify-content:center}
.h-search .hsearch{flex:1;max-width:36rem}
.h-search .hsearch input[type=search]{flex:1;width:auto}
@media (max-width:1080px){.h-search,.h-search-btn{display:none}}

/* ── Staff entry points (prototype v2 .adm-chip / .adm-link) ── */
.a11y .adm-chip{display:inline-flex;align-items:center;min-height:28px;background:var(--ink);color:var(--bg);border-radius:999px;padding:1px 12px;text-decoration:none;font-weight:600}
.adm-link{display:inline-flex;gap:6px;align-items:center;text-decoration:none;font-weight:600;color:var(--on-deep-accent)}

/* ── Footer info (partials/footer_info.html) ── */
/* anywhere (not break-word): it also lowers the min-content width, so the one-word
   association name wraps instead of pushing the page sideways at 195 CSS px (D17). */
footer .org-info{font-style:normal;display:flex;flex-wrap:wrap;gap:4px 0;min-width:0;overflow-wrap:anywhere}
footer .org-info span+span::before{content:"·";content:"·" / "";margin-inline:8px}
@media (max-width:640px){
  footer .org-info{display:grid;grid-template-columns:minmax(0,1fr);gap:2px}
  footer .org-info span+span::before{content:none}
  /* Every row the same height: rows holding a link (전화, 이메일) are 44px tall for the
     target size, so the plain-text rows match them (R6-RP-09). */
  footer .org-info span{display:flex;flex-wrap:wrap;align-items:center;column-gap:.3em;min-height:var(--target-min)}
  footer .org-info a{display:inline-flex;align-items:center;min-height:var(--target-min)}
}
footer .legal{display:flex;flex-wrap:wrap;gap:6px 16px;font-size:.8rem;color:var(--on-deep-2)}
footer .legal a{display:inline-flex;align-items:center;min-height:var(--target-min)}
footer .legal b{color:var(--on-deep)}

/* ── Site search page (apps/search) ── */
.search-wide{max-width:640px;margin-bottom:18px}
.search-wide input[type=search]{flex:1}
.search-status{font-weight:600;margin-bottom:22px}
.search-kinds{margin-bottom:28px}
.search-kinds .n{font-variant-numeric:tabular-nums;font-weight:500;opacity:.8}
.search-group+.search-group{margin-top:clamp(36px,5vw,56px)}
.result-list{border-top:2px solid var(--ink)}
.result-list li{position:relative;display:grid;grid-template-columns:minmax(0,1fr);gap:4px;padding:18px 4px;border-bottom:1px solid var(--line)}
/* The whole result row is the title link's hit area (::after), as the home cards (MP-13);
   the focus ring moves to that box. Links inside a snippet would sit above it (z-index). */
.result-title{font-weight:600;font-size:1.08rem;text-decoration:none}
.result-title::after{content:"";position:absolute;inset:0;border-radius:12px}
.result-title:focus-visible{outline:none;box-shadow:none}
.result-title:focus-visible::after{outline:3px solid var(--focus);outline-offset:-2px}
.result-list li a:not(.result-title){position:relative;z-index:1}
.result-list li:hover .result-title,.result-title:hover{text-decoration:underline;text-decoration-color:var(--yellow);text-decoration-thickness:3px}
.result-snippet{color:var(--ink-2);font-size:1rem}
/* 바로 가기 (menu shortcuts, R4-FIND-01): the section in a quieter weight before the page. */
.result-section{font-weight:400;color:var(--muted)}
/* A long unbroken title or snippet wraps inside its row instead of widening the page. */
.result-title,.result-snippet{overflow-wrap:anywhere}
.result-list .d{font-family:var(--mono);font-size:.82rem;color:var(--muted);font-variant-numeric:lining-nums tabular-nums}
mark{background:var(--yellow-soft);color:inherit;padding:0 2px;border-radius:4px;box-shadow:inset 0 -2px 0 var(--yellow)}
/* Empty state (partials/empty_state.html; CC-22): what is empty, an optional hint, then the
   next steps. .empty-actions is the one definition of that action row; apps must not copy it. */
/* minmax(0,1fr): an auto track grows to the longest unbreakable word, and body's
   overflow-wrap:break-word does not lower min-content; a 120-character query echoed in the
   heading pushed the page sideways at 320px (WCAG 1.4.10; RD-05). */
.empty-state{display:grid;grid-template-columns:minmax(0,1fr);gap:14px;padding-block:8px 12px}
.empty-state :is(.sec-t,.empty-msg),.search-status{overflow-wrap:anywhere}
.empty-state ul{margin:0;padding-left:1.2em}
.empty-state .empty-msg{font-weight:600}
.empty-actions{display:flex;flex-wrap:wrap;gap:8px;align-items:center}
.search-shortcuts{display:grid;gap:12px;margin-top:28px}
.search-shortcuts ul{display:flex;flex-wrap:wrap;gap:10px 22px}

/* ── Error pages (404/403/500) ── */
.error-page{display:grid;gap:18px;max-width:44em}
.error-page .search{max-width:520px}
.error-page .request-id{font-variant-numeric:tabular-nums;overflow-wrap:anywhere;user-select:all}

/* ── Form errors (partials/error_summary.html, partials/field_error.html) ──
   GOV.UK error summary + error message: what to fix first, each item linked to its
   field; inline messages repeat it next to the field (WCAG 3.3.1, 3.3.3, 2.4.3). The
   "!" mark is decorative (alt text ""), so colour is never the only cue (1.4.1). */
/* Failed htmx request (site.js, base.html): a bar pinned to the bottom with a retry link. */
.htmx-alert{position:fixed;z-index:90;left:50%;bottom:calc(16px + env(safe-area-inset-bottom,0px));transform:translateX(-50%);width:min(36rem,calc(100% - 32px));padding:14px 18px;border-radius:14px;background:var(--ink);color:var(--bg);border:2px solid var(--ink);box-shadow:0 18px 40px -20px var(--shadow);font-weight:600}
.htmx-alert:empty{padding:0;border:0;box-shadow:none}
.htmx-alert:not(:empty){display:flex;gap:12px;align-items:flex-start}
.htmx-alert p{flex:1;min-width:0;margin:0;overflow-wrap:anywhere}
.htmx-alert a{display:inline-flex;align-items:center;min-height:var(--target-min);color:inherit;text-underline-offset:3px;white-space:nowrap}
.htmx-alert :is(a,button):focus-visible{outline-color:var(--bg);box-shadow:0 0 0 2px var(--ink)}
.htmx-alert-close{flex-shrink:0;min-width:var(--target-min);min-height:var(--target-min);padding:0 12px;border:1.5px solid currentColor;border-radius:999px;background:transparent;color:inherit;font:inherit;cursor:pointer}
/* While the bar shows, the end of the page keeps room for it (its height: --alert-h, set by
   site.js; base.html is the only page with the bar, and it always ends with footer.site), so
   the last footer links can still scroll out from under it (R5-MT-01). */
html:has(.htmx-alert:not(:empty)){scroll-padding-bottom:calc(var(--alert-h,9rem) + 32px + env(safe-area-inset-bottom,0px))}
html:has(.htmx-alert:not(:empty)) footer.site{padding-bottom:calc(36px + var(--alert-h,9rem) + 32px + env(safe-area-inset-bottom,0px))}
.error-summary{display:grid;gap:6px;padding:16px 20px;border-radius:14px;background:var(--yellow-soft);color:var(--ink);border:2px solid var(--ink)}
.error-summary-body{display:grid;gap:6px;min-width:0}
.error-summary:focus{outline:none}
.error-summary:focus-visible{outline:3px solid var(--focus);outline-offset:3px}
.error-summary-title{font-size:1.05rem;font-weight:700;line-height:1.4}
.error-summary ul{margin:0;padding-left:1.2em;display:grid;gap:4px}
.error-summary a{color:inherit;font-weight:600;text-underline-offset:3px}
.error-summary a:hover{text-decoration-thickness:3px;text-decoration-color:var(--yellow-ink)}
.field-error,.errorlist{display:grid;gap:2px;margin:0;padding:0;list-style:none;font-size:.92rem;font-weight:700;color:var(--ink)}
.field-error p,.errorlist li{margin:0}
.field-error p::before{content:"! ";content:"! " / "";color:var(--yellow-ink)}
.errorlist li::before{content:"! ";content:"! " / "오류: ";color:var(--yellow-ink)}  /* Django's list has no hidden "오류:" */
.has-error{padding-left:14px;border-left:4px solid var(--yellow-ink)}
.has-error :is(input:not([type=checkbox],[type=radio]),select,textarea),[aria-invalid="true"]:is(input,select,textarea){border-color:var(--ink);border-width:2px}
@media (forced-colors:active){
  .error-summary{border-color:CanvasText}
  .htmx-alert{border-color:CanvasText}
  .htmx-alert-close{border-color:ButtonText}
  .has-error{border-left-color:CanvasText}
  .has-error :is(input,select,textarea){border-color:CanvasText}
}

/* ── Document dialog (partials/dialog.html; design §5) ──
   An engine that does not know <dialog> must never show closed content inline. Head and
   foot stay put, the body scrolls; the page behind does not (html.has-dialog, site.js).
   Enter: a short fade and settle from .97 (modals stay centred); exit is instant, since
   Esc is a keyboard action. Reduced motion keeps only the fade. */
dialog:not([open]){display:none}
html.has-dialog{overflow:hidden}
dialog.doc-dialog{width:min(720px,calc(100vw - 32px));max-width:none;max-height:calc(100vh - 32px);max-height:calc(100dvh - 32px);display:none;flex-direction:column;overflow:hidden;border:1px solid var(--line);border-radius:var(--r);box-shadow:0 24px 64px -24px var(--shadow)}
dialog.doc-dialog[open]{display:flex;opacity:1;transform:none;transition:opacity var(--dur-panel) var(--ease-out),transform var(--dur-panel) var(--ease-out)}
@starting-style{dialog.doc-dialog[open]{opacity:0;transform:scale(.97)}}
dialog.doc-dialog::backdrop{background:rgba(5,12,8,.72);-webkit-backdrop-filter:none;backdrop-filter:none}
@media (prefers-reduced-motion:reduce){dialog.doc-dialog[open]{transition:opacity var(--dur-panel) ease}@starting-style{dialog.doc-dialog[open]{transform:none}}}
:root[data-motion="off"] dialog.doc-dialog[open]{transform:none}
.doc-dialog-head,.doc-dialog-foot{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:14px 18px 14px 24px;flex:none}
.doc-dialog-head{border-bottom:1px solid var(--line)}
.doc-dialog-foot{border-top:1px solid var(--line);flex-wrap:wrap}
.doc-dialog-title{font-family:var(--serif);font-size:1.3rem;font-weight:600;line-height:1.35;min-width:0;overflow-wrap:anywhere}
.doc-dialog-title:focus{outline:none}
.doc-dialog-title:focus-visible{outline:3px solid var(--focus);outline-offset:4px}
.doc-dialog-body{flex:1 1 auto;min-height:0;overflow:auto;overscroll-behavior:contain;padding:18px 24px 24px}
.doc-dialog-body:focus-visible{outline:3px solid var(--focus);outline-offset:-3px}
.doc-dialog-page{display:inline-flex;align-items:center;min-height:var(--target-min);font-weight:600;color:var(--ink);text-underline-offset:3px}
/* Short viewports (400 % zoom: 320×256 CSS px; WCAG 1.4.10/1.4.4): fixed head and foot
   would leave the text a strip of a line or two, so the whole dialog scrolls as one block
   and the second "닫기" is dropped (the head keeps one). */
@media (max-height:30rem){
  dialog.doc-dialog{overflow:auto;overscroll-behavior:contain}
  .doc-dialog-body{flex:none;overflow:visible;min-height:auto}
  .doc-dialog-head,.doc-dialog-foot{position:static}
  .doc-dialog-close-end{display:none}
}
/* Legal text inside a dialog or its fallback page (apps.association.legal: headings from
   h3, captioned tables with th[scope], definition lists). */
:is(.doc-dialog-body,.doc-body){font-size:.95rem;line-height:1.75;color:var(--ink-2);overflow-wrap:anywhere}
:is(.doc-dialog-body,.doc-body) :is(h3,h4,h5){color:var(--ink);font-weight:700;line-height:1.4;margin:1.4em 0 .4em}
:is(.doc-dialog-body,.doc-body) h3{font-size:1.08rem}
:is(.doc-dialog-body,.doc-body) :is(h4,h5){font-size:1rem}
:is(.doc-dialog-body,.doc-body) > :first-child{margin-top:0}
:is(.doc-dialog-body,.consent-document) .draft-notice{display:flex;margin:0 0 1em;font-family:var(--sans)}
:is(.doc-dialog-body,.doc-body) :is(p,ul,ol,dl,table){margin:0 0 .8em}
:is(.doc-dialog-body,.doc-body) :is(ul,ol){padding-left:1.3em}
:is(.doc-dialog-body,.doc-body) strong{color:var(--ink)}
:is(.doc-dialog-body,.doc-body) dl{display:grid;grid-template-columns:minmax(5.5em,auto) minmax(0,1fr);gap:6px 14px}
:is(.doc-dialog-body,.doc-body) dt{font-weight:700;color:var(--ink)}
:is(.doc-dialog-body,.doc-body) dd{margin:0}
:is(.doc-dialog-body,.doc-body) table{display:block;max-width:100%;overflow-x:auto;border-collapse:collapse;font-size:.9rem}
:is(.doc-dialog-body,.doc-body) caption{text-align:left;font-weight:700;color:var(--ink);padding:0 0 6px}
:is(.doc-dialog-body,.doc-body) :is(th,td){border:1px solid var(--line-strong);padding:6px 10px;text-align:left;vertical-align:top}
:is(.doc-dialog-body,.doc-body) th{background:var(--sunk);color:var(--ink)}
@media (max-width:420px){:is(.doc-dialog-body,.doc-body) dl{grid-template-columns:minmax(0,1fr);gap:0}:is(.doc-dialog-body,.doc-body) dd{margin-bottom:8px}}
/* "보기" opener: a small outlined pill, a link without JS and a button with it. */
.dialog-opener{display:inline-flex;align-items:center;justify-content:center;flex:none;min-height:var(--target-min);min-width:var(--target-min);padding:0 14px;border:1.5px solid var(--line-strong);border-radius:999px;background:var(--surface);color:var(--ink);font:inherit;font-size:.86rem;font-weight:600;line-height:1;text-decoration:none;cursor:pointer;touch-action:manipulation;transition:transform var(--dur-press) var(--ease-out),border-color var(--dur-ui) ease}
.dialog-opener:active{transform:scale(.97)}
@media (hover:hover) and (pointer:fine){.dialog-opener:hover{border-color:var(--ink)}}
.dialog-opener:focus-visible{outline:3px solid var(--focus);outline-offset:2px}
@media (prefers-reduced-motion:reduce){.dialog-opener:active{transform:none}}
:root[data-motion="off"] .dialog-opener:active{transform:none}
@media (prefers-reduced-motion:reduce){.hero-pause button:active{transform:none}}
:root[data-motion="off"] .hero-pause button:active{transform:none}
@media (forced-colors:active){
  dialog.doc-dialog{border:1px solid CanvasText}
  .dialog-opener{border:1px solid ButtonText}
  :is(.doc-dialog-body,.doc-body) :is(th,td){border-color:CanvasText}
}

/* ── Phones: information at a readable size (ADR-0017 decision 13, render check 2026-10-14) ──
   Chips, dates, bylines, counts, the breadcrumb, hints, field labels and small buttons carry
   what a reader acts on, so on a phone none is under 15px at normal text (.9375rem; it grows
   with the text-size setting). The apps' own rules (home, community, media, literature,
   association, accounts) keep the same floor; test_phone_text_browser.py checks it. */
@media (max-width:640px){
  .crumb,.help,.chip,.cat,.badges span,.dur,.card .num,.timeline dt,.result-list .d,.work .tags,.work .by,.toc .p,.toc .g,.gallery figcaption small,.btn.sm,.dialog-opener,.field label,.field legend,.eyebrow.k,.edit-note b,.shelf span,.hero-pause button{font-size:.9375rem}
}
