/* BORYSU Web MVP-01. Desktop-first, one breakpoint to stop mobile from breaking. */
:root {
  --bg: #f5f6f8;
  --card: #ffffff;
  --ink: #1c2128;
  --muted: #5b6673;
  --line: #dde1e6;
  --brand: #1b5fbe;
  --brand-dark: #17509f;
  --ok-bg: #e7f4ec; --ok-ink: #17683a;
  --warn-bg: #fdf3dd; --warn-ink: #8a6100;
  --bad-bg: #fdeceb; --bad-ink: #a12822;
}

* { box-sizing: border-box; }

/* The `hidden` attribute only works through the UA stylesheet's
   `[hidden] { display: none }`, which any author `display` rule outranks --
   `.modal` below sets `display: flex`, which silently made `hidden` inert on
   it: the empty dialog rendered on page load and setting `.hidden = true` did
   nothing visible. Declaring it here, with !important, makes the attribute
   authoritative for every element on the page rather than only the ones that
   happen not to set a display. */
[hidden] { display: none !important; }

/* Scroll lock while a dialog is open, so the page behind it cannot move. */
body.modal-open { overflow: hidden; }

body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Apple SD Gothic Neo",
               "Malgun Gothic", "Noto Sans KR", sans-serif;
  background: var(--bg);
  color: var(--ink);
  line-height: 1.6;
}

.wrap { max-width: 980px; margin: 0 auto; padding: 0 20px; }

/* One row on a desktop: brand, description, the four destinations, then the
   member's controls on the right. It used to stack those on three lines and
   cost roughly 90px of height on every page. The colour and the controls are
   unchanged; only the vertical space is. */
.site-header {
  background: var(--brand);
  color: #fff;
  padding: 10px 0;
  margin-bottom: 18px;
}
/* Three zones, across the whole window rather than inside the page column.
   `auto 1fr auto` gives the brand and the member controls exactly the width
   they need and hands the rest to the middle, so the nav sits in the centre of
   the screen at 1920px and at 1280px alike. The old flex row with a spacer put
   all three inside .wrap's 980px and left a third of the screen empty on each
   side. */
.header-row {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 16px;
  /* Full width, but never touching the glass. */
  max-width: none;
  margin: 0;
  padding: 0 24px;
}
.header-row > .brand { justify-self: start; }
.header-row > .header-center { justify-self: center; }
.header-row > .nav-account { justify-self: end; }

/* The middle zone holds the destinations. The description used to share it;
   a line of prose in a row of controls cost the menus the width they needed,
   and it belongs on the page somebody lands on, not on all of them. */
.header-center {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  row-gap: 6px;
  justify-content: center;
  min-width: 0;
}
.site-header .brand {
  margin: 0; font-size: 20px; font-weight: 700; letter-spacing: 1px;
  color: #fff; text-decoration: none; white-space: nowrap;
}
/* Smaller than the brand and allowed to disappear first: on a narrow screen the
   description is the one thing here nobody needs to act on. */
.service-name {
  margin: 0; font-size: 13px; font-weight: 500; opacity: .9;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  min-width: 0;
}

.card {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 20px 22px;
  margin-bottom: 20px;
}
.card h2 { margin: 0 0 6px; font-size: 18px; }
.card h3 { margin: 20px 0 8px; font-size: 15px; border-top: 1px solid var(--line); padding-top: 14px; }

.hint { margin: 0 0 14px; color: var(--muted); font-size: 13px; }
.hint-inline { color: var(--muted); font-weight: 400; font-size: 12px; }

/* ---- presets ---- */
.preset-row { display: flex; flex-wrap: wrap; gap: 8px; }
.preset {
  background: #eef2f7; border: 1px solid var(--line); color: var(--ink);
  padding: 7px 13px; border-radius: 999px; font-size: 13px; cursor: pointer;
}
.preset:hover { background: #e2e9f2; border-color: #c3ccd8; }
.preset.active { background: var(--brand); border-color: var(--brand); color: #fff; }
.preset.reset { background: #fff; color: var(--muted); }
.preset-active { margin: 12px 0 0; font-size: 13px; color: var(--muted); }

/* ---- form ---- */
.grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px 16px; }
.grid.tri { grid-template-columns: repeat(3, 1fr); }
label { display: block; font-size: 13px; font-weight: 600; }
label.full { display: block; margin-top: 12px; }
input, select, textarea {
  width: 100%; margin-top: 4px; padding: 8px 10px;
  border: 1px solid var(--line); border-radius: 6px;
  font: inherit; font-weight: 400; background: #fff; color: var(--ink);
}
input:focus, select:focus, textarea:focus { outline: 2px solid var(--brand); outline-offset: -1px; }
input.invalid { border-color: #c0392b; background: #fff7f6; }
textarea { resize: vertical; }
.req { color: #c0392b; }

.actions { margin-top: 20px; display: flex; align-items: center; gap: 14px; }
/* An <a> gets the same button as a <button> does. The rule used to be
   `button.primary` alone, so `<a class="primary">` -- the board's write action
   -- picked up none of it and rendered as a plain blue link, which is exactly
   how it read on the page. One rule, both elements, one set of colours. */
button.primary, a.primary {
  background: var(--brand); color: #fff; border: 0; border-radius: 7px;
  padding: 11px 24px; font-size: 15px; font-weight: 600; cursor: pointer;
}
a.primary { display: inline-block; text-decoration: none; line-height: 1.2; }
button.primary:hover, a.primary:hover { background: var(--brand-dark); }
button.primary:focus-visible, a.primary:focus-visible {
  outline: 2px solid var(--brand-dark); outline-offset: 2px;
}
button.primary:disabled { background: #9aa7b6; cursor: progress; }
.status { font-size: 14px; color: var(--muted); }

.errors {
  margin-top: 14px; padding: 10px 12px; border-radius: 6px;
  background: var(--bad-bg); color: var(--bad-ink); font-size: 13px;
}
.errors ul { margin: 4px 0 0; padding-left: 18px; }

/* ---- results ---- */
.gate-summary { color: var(--muted); font-size: 13px; margin: 0 0 14px; }
.result {
  border: 1px solid var(--line); border-radius: 8px;
  padding: 14px 16px; margin-bottom: 10px; background: #fff;
}
.result-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; }
.result-title { margin: 0; font-size: 15px; font-weight: 600; }
.result-meta { color: var(--muted); font-size: 13px; margin: 6px 0 0; }
.result-why { font-size: 13px; margin: 8px 0 0; }
.result-actions { margin-top: 10px; }
.link-btn {
  background: none; border: 1px solid var(--line); border-radius: 6px;
  padding: 6px 12px; font-size: 13px; cursor: pointer; color: var(--brand);
}
.link-btn:hover { background: #f0f4fa; }

.badge {
  display: inline-block; padding: 3px 10px; border-radius: 999px;
  font-size: 12px; font-weight: 600; white-space: nowrap;
}
.badge.ok { background: var(--ok-bg); color: var(--ok-ink); }
.badge.warn { background: var(--warn-bg); color: var(--warn-ink); }
.badge.bad { background: var(--bad-bg); color: var(--bad-ink); }

.empty { padding: 22px; text-align: center; color: var(--muted); font-size: 14px; }

/* ---- one page of results ---- */
/* The toolbar carries the count on the left and the size selector on the
   right; it wraps rather than squeezing either one on a narrow screen. */
.results-toolbar {
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap; gap: 6px 14px; margin: 0 0 14px;
}
.results-summary { margin: 0; font-size: 13px; color: var(--muted); }
.results-toolbar label.page-size-picker {
  font-size: 13px; font-weight: 400; color: var(--muted);
  display: inline-flex; align-items: center; gap: 6px; margin-left: auto;
}
.results-toolbar label.page-size-picker select { width: auto; margin-top: 0; }

.pagination {
  margin-top: 18px; display: flex; align-items: center; justify-content: center;
  flex-wrap: wrap; gap: 6px;
}
/* The numbers are the part that can grow, so they get their own box: it wraps
   first and scrolls only if a line still will not fit. Nothing here may push
   the page sideways on a phone. */
.page-numbers {
  display: flex; align-items: center; justify-content: center;
  flex-wrap: wrap; gap: 4px; max-width: 100%; overflow-x: auto;
}
.page-step, .page-num {
  background: #fff; border: 1px solid var(--line); border-radius: 6px;
  color: var(--ink); padding: 6px 11px; font-size: 13px; cursor: pointer;
  min-width: 36px; line-height: 1.2;
}
.page-step:hover:not(:disabled), .page-num:hover:not(.active) { background: #f0f4fa; }
.page-step:disabled { opacity: .4; cursor: default; }
.page-num.active {
  background: var(--brand); border-color: var(--brand); color: #fff; font-weight: 600;
}
.page-num:disabled { cursor: default; }
.page-gap { color: var(--muted); font-size: 13px; padding: 0 2px; }

/* ---- application period ---- */
/* The period badge is deliberately a different shape from the eligibility
   badge (square-ish, outlined) so the two are not read as one verdict. */
.badge-row { display: flex; flex-wrap: wrap; gap: 6px; margin: 8px 0 0; }
.badge.period {
  border-radius: 5px; border: 1px solid transparent;
  background: #eef2f7; color: var(--muted);
}
.badge.period.open    { background: #eaf2fd; color: #14508f; border-color: #c3d9f5; }
.badge.period.soon    { background: #f1eefb; color: #4b3b8f; border-color: #d6cdf0; }
.badge.period.urgent  { background: #fdeceb; color: #a12822; border-color: #f3c7c4; font-weight: 700; }
.badge.period.closed  { background: #eceef1; color: #5b6673; border-color: #d7dbe0; }
.badge.period.check   { background: #f5f2e8; color: #715c22; border-color: #e3d9bd; }

.result-period, .detail-period {
  margin: 8px 0 0; font-size: 13px; font-weight: 600; color: var(--ink);
}
.detail-period { margin: 10px 0 14px; font-size: 14px; }
.detail-agency { margin: 2px 0 0; color: var(--muted); font-size: 14px; }

/* A closed programme stays visible -- eligibility and recruitment status are
   different questions -- but reads as past. */
.result.is-closed { background: #fafbfc; }
.result.is-closed .result-title { color: var(--muted); }

.result-actions { display: flex; align-items: center; gap: 12px; }
.result-actions .result-meta { margin: 0; font-size: 12px; }

/* ---- modal ---- */
.modal { position: fixed; inset: 0; z-index: 50; display: flex; align-items: center; justify-content: center; }
.modal-backdrop { position: absolute; inset: 0; background: rgba(20, 26, 33, .5); }
.modal-body {
  position: relative; background: #fff; border-radius: 10px;
  width: min(760px, calc(100vw - 40px)); max-height: calc(100vh - 60px);
  overflow-y: auto; padding: 24px 26px;
}
.modal-close {
  position: absolute; top: 10px; right: 14px; border: 0; background: none;
  font-size: 26px; line-height: 1; cursor: pointer; color: var(--muted);
}
.detail-section { margin-top: 20px; }
.detail-section h4 { margin: 0 0 8px; font-size: 14px; }
.detail-section p { margin: 0 0 8px; font-size: 14px; white-space: pre-wrap; }
.kv { font-size: 14px; margin: 0 0 6px; }
.kv .k { color: var(--muted); display: inline-block; min-width: 84px; }

.cond { display: flex; justify-content: space-between; gap: 10px;
        padding: 7px 0; border-bottom: 1px solid var(--line); font-size: 14px; }
.cond:last-child { border-bottom: 0; }
.cond-label { font-weight: 600; }
.cond-detail { color: var(--muted); font-size: 13px; }

.evidence {
  border-left: 3px solid var(--brand); background: #f7f9fc;
  padding: 10px 12px; margin-bottom: 10px; border-radius: 0 6px 6px 0;
}
.evidence blockquote { margin: 0 0 6px; font-size: 13px; }
.evidence .src { font-size: 12px; color: var(--muted); }

.xai-note, .evidence-note {
  font-size: 12px; color: var(--muted); margin: 0 0 10px;
  padding: 8px 10px; border-radius: 6px; background: #f0f2f5;
}

.footer { color: var(--muted); font-size: 12px; padding-bottom: 30px; }

@media (max-width: 720px) {
  .grid, .grid.tri { grid-template-columns: 1fr; }
  .result-head { flex-direction: column; }
  .preset-row { gap: 6px; }
  .preset { font-size: 12px; padding: 6px 10px; }
  /* The count and the selector each get their own line rather than the two
     fighting over one. */
  .results-toolbar { flex-direction: column; align-items: flex-start; }
  .results-toolbar label.page-size-picker { margin-left: 0; }
  .page-step, .page-num { padding: 6px 9px; min-width: 32px; font-size: 12px; }
}

/* ---- navigation ---- */
.site-nav { display: flex; gap: 6px; flex-wrap: wrap; row-gap: 6px; }
.nav-link {
  color: #fff; text-decoration: none; font-size: 13px; padding: 5px 11px;
  border-radius: 999px; background: rgba(255,255,255,.14);
  white-space: nowrap;
}
.nav-link:hover { background: rgba(255,255,255,.24); }
.nav-link.active { background: #fff; color: var(--brand); font-weight: 600; }

/* ---- explorer ---- */
.filter-heading { margin: 18px 0 8px; font-size: 14px; border-top: 1px solid var(--line); padding-top: 14px; }
.preset.filter { font-size: 13px; }
.explorer-options { margin-top: 18px; display: flex; gap: 20px; align-items: center; flex-wrap: wrap; }
label.inline { display: inline-flex; align-items: center; gap: 8px; font-size: 13px; }
label.inline select { width: auto; margin-top: 0; }
label.inline.checkbox input { width: auto; margin: 0; }
.badge.purpose { background: #e8eefb; color: #24408a; border-radius: 5px; }
.result-agency { margin: 6px 0 0; color: var(--muted); font-size: 13px; }
.detail-footnote { margin-top: 18px; font-size: 12px; color: var(--muted); }
.pager { margin-top: 16px; display: flex; align-items: center; justify-content: center; gap: 14px; font-size: 13px; }
.pager button:disabled { opacity: .4; cursor: default; }

/* ---- 지원목적 / 마감일, on the recommendation list ---- */
/* The buttons themselves are the explorer's: .preset.filter and .preset.filter
   .active, defined once above. Only the block that holds them is new -- it
   opens the results card, where the first .filter-heading must not draw a rule
   above itself the way it does mid-card on /programs. */
.rec-filters { margin: 0 0 16px; }
.rec-filters .filter-heading:first-child {
  margin-top: 0; border-top: none; padding-top: 0;
}
.rec-filters .preset.filter:disabled { opacity: .55; cursor: default; }

/* ---- member navigation and profile saving ---- */
.nav-spacer { flex: 1; }
/* The nav has to wrap. /account gained the member area when the header was
   unified, so the row is now four links plus an address plus two controls --
   without wrapping that runs off the side of a phone. */
.site-nav { align-items: center; flex-wrap: wrap; row-gap: 8px; }
/* 관리자 · 내 정보 · 이용안내 · 로그아웃, on one line at the right. The address
   that used to sit in front of them is gone, which is also why the rule that
   stopped a long one from pushing these buttons off-screen is gone with it. */
.nav-account { display: flex; gap: 8px; align-items: center; min-width: 0; }

/* The centre zone carries seven destinations. Before the row would have to
   wrap the links tighten, which is what keeps brand, menus and member controls
   on one line rather than dropping the member area onto a row of its own. The
   description is no longer here to give up first, so the links now do that
   work a little earlier. */
@media (max-width: 1280px) {
  .site-nav { gap: 4px; }
  .nav-link { padding: 5px 9px; font-size: 12.5px; }
}
@media (max-width: 1080px) {
  .nav-link { padding: 4px 8px; font-size: 12px; }
  .header-row { gap: 10px; padding: 0 14px; }
}

@media (max-width: 560px) {
  /* Below this one row stops fitting. Letting it wrap is the point -- forcing
     the row would push content off the side, and a header you have to scroll
     sideways to read is worse than a taller one. The grid collapses to a
     single column and each zone takes its own line. */
  .header-row {
    grid-template-columns: 1fr;
    justify-items: center;
    padding: 0 14px;
    row-gap: 8px;
  }
  .header-row > .brand,
  .header-row > .header-center,
  .header-row > .nav-account { justify-self: center; }
  .nav-spacer { flex-basis: 100%; height: 0; }
  .header-search-row { padding: 8px 14px 0; }
}

button.secondary {
  background: #fff; color: var(--brand); border: 1px solid var(--brand);
  border-radius: 7px; padding: 10px 18px; font-size: 14px; font-weight: 600;
  cursor: pointer;
}
button.secondary:hover { background: #eef3fb; }
button.secondary:disabled { opacity: .55; cursor: default; }
.save-note { margin: 10px 0 0; font-size: 13px; }
.save-note.ok { color: var(--ok-ink); }
.save-note.warn { color: var(--warn-ink); }
.save-note.bad { color: var(--bad-ink); }

/* The service tagline, on the home page only. It was the header's third line
   until the header became one row; the sentence still belongs on the page a
   visitor lands on. */
.page-tagline {
  margin: 0 0 18px; font-size: 15px; color: #4b5563;
}

/* The console entry in the header. Marked out from the member links because it
   goes somewhere different in kind, not just somewhere else. */
.nav-link.nav-admin {
  background: #fff; color: var(--brand); font-weight: 700;
  border: 1px solid rgba(255,255,255,.6);
}
.nav-link.nav-admin:hover { background: #eef4fd; }

/* --------------------------------------------------------------- 탐색 모드 */
/* Four lists share one card, so the card has to say which one is in it -- and
   it says it in the heading, not in a pill beside one. A reader scrolling back
   up has to be able to see what they are looking at before finding a row, which
   is a job for the h2. #result-mode-badge IS that h2; app.js writes its text
   from the response that filled the list. The rules live in the workspace block
   at the end of this file, beside the layout they belong to. */
.results-head h2 { margin: 0; }

.mode-explainer {
  margin: 14px 0 0; padding: 12px 14px; font-size: 13px; line-height: 1.7;
  background: #f7f9fc; border: 1px solid var(--line); border-radius: 8px;
  color: #44506a;
}
.mode-explainer strong { color: var(--ink); }

/* A FAST row and a browse row wear a badge saying so, so a relevance-only
   card cannot be mistaken for a judged one at a glance. */
.badge-fast { background: #eef6ee; color: #2f6b3a; border: 1px solid #cfe4d2; }
.badge-browse { background: #fdf3e7; color: #8a5a17; border: 1px solid #f0dcc0; }
.result-note { font-size: 12px; color: var(--muted); margin: 6px 0 0; }

/* 관련도 is a band, not a percentage: the FAST score measures resemblance to
   what the company wrote, and a number beside an unjudged notice reads as a
   chance of being accepted. */
.badge-relevance { background: #eef2fb; color: #33457c; border: 1px solid #d3dcf2; }

/* ====================================================== 맞춤 추천 워크스페이스 */
/* Three columns on a desktop: the page's own menu, the list, the company form.
   Only the home page uses this; every other page keeps .wrap's 980px column,
   which is the right width for prose and the wrong one for a ranked list.

   The old single column put 테스트 기업 자동입력 and the whole company form above
   the results, so the list a member came for started roughly 1,400px down. What
   moved is where the form is drawn. Nothing here changes a name, a value or a
   request. */
/* Also 지원사업 탐색's: a one-line list uses the width a 1920px screen has
   rather than leaving 470px blank on each side of a 980px column. */
.workspace-wrap { max-width: 1880px; }

.workspace {
  display: grid;
  /* 좌 200 / 중앙 나머지 전부 / 우 400. minmax(0, 1fr) rather than 1fr so a long
     unbreakable notice title cannot widen the track and push the page sideways --
     1fr's min-content floor is exactly how a grid grows past its container. */
  grid-template-columns: 200px minmax(0, 1fr) 400px;
  gap: 20px;
  /* Each column starts at the top instead of stretching to the tallest, which
     is what lets the two side columns be sticky. */
  align-items: start;
}

/* Only the menu sticks. The header is not sticky -- it scrolls away with the
   page -- so a sticky column has nothing to collide with and needs no
   header-height offset; 14px is breathing room against the viewport edge.
   The menu is seven short rows and always fits, which is the condition sticky
   needs. */
.ws-menu { position: sticky; top: 14px; }

/* The company panel does NOT stick, and the earlier claim here that a sticky box
   taller than the viewport "simply scrolls with the page" was wrong.
   `position: sticky; top: 14px` holds the element's TOP at 14px for as long as
   it is inside its grid area -- so when the element is taller than the viewport,
   its bottom sits below the fold and scrolling never brings it up: the box moves
   with the scroll to keep its top pinned.
   The last things in this panel are 회사정보 저장 and 정밀 추천 실행, so that is
   precisely what became unreachable -- certainly so once 재무정보 or 참여제한 is
   expanded, which is what "기업정보를 확인한 후" invites a member to do. A panel
   that scrolls with the page is worth more than one that keeps its heading in
   view and its buttons out of reach. */
.ws-side { display: flex; flex-direction: column; }
.ws-main { min-width: 0; }

/* ---- 좌측 페이지 메뉴 ---- */
.ws-menu-nav {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 12px 10px;
}
.ws-menu-group {
  margin: 12px 6px 6px; font-size: 11px; font-weight: 700; letter-spacing: .04em;
  color: var(--muted); text-transform: none;
}
.ws-menu-group:first-child { margin-top: 2px; }
.ws-menu-item {
  display: block; width: 100%; text-align: left;
  background: none; border: 0; border-radius: 7px;
  padding: 8px 10px; font: inherit; font-size: 13.5px; color: var(--ink);
  cursor: pointer;
}
.ws-menu-item:hover { background: #eef3fb; }
.ws-menu-item:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }
.ws-menu-item.active {
  background: var(--brand); color: #fff; font-weight: 600;
}
/* Not yet run, so it has no result to show. It stays pressable -- pressing it
   takes the member to the button that starts one, which is more use than a
   dead control. Greyed only while it is not the selected item: chosen, it is
   white on blue like every other selected item -- this rule used to win over
   .active and leave 정밀 추천 dark on blue. */
.ws-menu-item.is-pending:not(.active) { color: var(--muted); }

/* ---- 중앙 ---- */
/* The card names itself in small type and the heading names the list in it, so
   the two lines read 추천사업 목록 / 빠른 추천 rather than repeating one of them. */
.results-head { display: block; }
.results-eyebrow {
  margin: 0 0 2px; font-size: 12px; font-weight: 600; letter-spacing: .02em;
  color: var(--muted);
}
/* #result-mode-badge is the card's h2. app.js rewrites its class on every mode
   change, so the size has to come from the class it sets, not from the element. */
.card h2.mode-badge, .mode-badge {
  display: block; margin: 0; padding: 0; border: 0; background: none;
  border-radius: 0; font-size: 19px; font-weight: 700; color: var(--ink);
}
/* 정밀 추천 결과 is the one list that carries a verdict, so it is the one that
   is coloured; the relevance-only lists stay neutral. */
.card h2.mode-badge.mode-precise, .mode-badge.mode-precise { color: #14508f; }
.mode-hint { margin: 6px 0 12px; }

/* The chip row and the column are the same control at two widths; exactly one
   of them is on screen. Above the breakpoint the column has it. */
.explore-modes { display: none; }

/* ---- 우측 기업정보 패널 ---- */
.ws-side .card { margin-bottom: 14px; }
.ws-side .card:last-child { margin-bottom: 0; }
.side-card { padding: 16px 16px 18px; }
.ws-side .card h2 { font-size: 16px; margin: 0 0 10px; }
/* 400px is not three columns of inputs. Two, and one for the 결격사유 selects,
   which are short enough. */
.ws-side .grid { grid-template-columns: repeat(2, 1fr); gap: 10px 12px; }
.ws-side .grid.tri { grid-template-columns: repeat(2, 1fr); }
.ws-side label { font-size: 12.5px; }
.ws-side input, .ws-side select, .ws-side textarea { padding: 7px 9px; font-size: 13px; }
.ws-side .hint { font-size: 12px; margin: 0 0 10px; }
.ws-side .mode-explainer { font-size: 12px; line-height: 1.65; padding: 10px 12px; }

/* ---- 접이식 그룹 ---- */
/* <details>, not a widget: no library, no script, and a closed group still
   holds its values and still submits them -- which is why the form could be
   grouped at all without touching what it sends. */
.ws-group { border-top: 1px solid var(--line); padding: 10px 0 2px; }
.ws-group:first-of-type { border-top: 0; padding-top: 0; }
.ws-group > summary {
  cursor: pointer; font-size: 14px; font-weight: 700; padding: 4px 0;
  list-style: none; display: flex; align-items: center; gap: 6px;
}
.ws-group > summary::-webkit-details-marker { display: none; }
.ws-group > summary::before {
  content: "▸"; color: var(--muted); font-size: 11px; width: 10px;
}
.ws-group[open] > summary::before { content: "▾"; }
.ws-group > summary:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.ws-group > *:not(summary) { margin-top: 10px; }
/* A group holding a field that failed validation is opened by app.js; this is
   what tells the reader which one it was. */
.ws-group.has-invalid > summary { color: var(--bad-ink); }

/* 테스트 기업 자동입력: demo data, so one collapsed line rather than the first
   card on the page. Every preset button is still inside it. */
.preset-card { padding: 12px 16px; }
.side-summary {
  cursor: pointer; font-size: 14px; font-weight: 700;
  list-style: none; display: flex; align-items: center; gap: 6px;
}
.side-summary::-webkit-details-marker { display: none; }
.side-summary::after { content: "▾"; color: var(--muted); font-size: 11px; }
details[open] > .side-summary::after { content: "▴"; }
.side-summary:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.preset-card[open] { padding-bottom: 16px; }
.preset-card > *:not(summary) { margin-top: 10px; }
.preset-card .preset { font-size: 12px; padding: 6px 10px; }

/* The two actions sit one above the other in a 400px column rather than being
   squeezed side by side. 저장 first, then the run -- the order the panel reads. */
.ws-side .actions {
  margin-top: 16px; display: grid; grid-template-columns: 1fr; gap: 8px;
}
.ws-side .actions button { width: 100%; }
.ws-side .actions .status { font-size: 12.5px; }
.save-hint { margin: 8px 0 0; font-size: 12px; }
/* The reason 정밀 추천 실행 cannot run, when it cannot. Reads as an instruction
   rather than an error: nothing has gone wrong, there is just nothing saved to
   rank against yet. */
.precise-note { margin: 8px 0 0; font-size: 12px; color: var(--warn-ink); }

/* ------------------------------------------------------------ 중간 폭 */
/* Under ~1400 the side column is what gives, because the list is the point of
   the page. The menu tightens with it rather than wrapping its labels. */
@media (max-width: 1400px) {
  .workspace { grid-template-columns: 176px minmax(0, 1fr) 340px; gap: 16px; }
  .ws-menu-item { font-size: 13px; padding: 7px 9px; }
  .ws-side .grid, .ws-side .grid.tri { grid-template-columns: 1fr 1fr; gap: 8px 10px; }
}

/* ------------------------------------------------- 태블릿 이하: 한 열 */
/* One column, and the order is the DOM order: the list, then the form.

   1180 is where the header already tightens, and it is where the narrowest
   useful three-column centre runs out -- 176 + 340 of side columns plus the gaps
   leaves under 600px for a notice title.

   The menu column is hidden rather than stacked. Stacked it is a block a member
   has to scroll past to reach the list, which is the problem this layout was
   built to fix; the chip row inside the card takes over as the mode switcher at
   exactly this breakpoint, so nothing becomes unreachable. */
@media (max-width: 1180px) {
  .workspace { grid-template-columns: minmax(0, 1fr); gap: 14px; }
  .ws-menu { display: none; position: static; top: auto; }
  .explore-modes { display: flex; margin: 0 0 4px; }
  /* Back to the page's own widths: the panel is full width again, so the
     desktop column's tighter type and two-up grid no longer apply. */
  .ws-side .grid { grid-template-columns: repeat(3, 1fr); }
  .ws-side .grid.tri { grid-template-columns: repeat(3, 1fr); }
  .ws-side label { font-size: 13px; }
  .ws-side .actions { grid-template-columns: auto auto 1fr; align-items: center; gap: 12px; }
  .ws-side .actions button { width: auto; }
}

/* 720px is where .grid already collapses to one column for the whole site. */
@media (max-width: 720px) {
  .workspace-wrap { padding: 0 14px; }
  .ws-side .grid, .ws-side .grid.tri { grid-template-columns: 1fr; }
  .ws-side .actions { grid-template-columns: 1fr; }
  .ws-side .actions button { width: 100%; }
  .card h2.mode-badge, .mode-badge { font-size: 17px; }
}

/* ---- 소재지: 시·도 / 시·군·구 ---- */
/* Two selects where there was one text field. Side by side at the panel's width
   and stacked when it narrows, because a half-width select cannot show
   "경상남도" and "창원시" is worse than useless truncated. */
.region-field { margin-top: 12px; }
.region-picker { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-top: 4px; }
.region-picker select { margin-top: 0; }
/* A province with no 시·군·구 to choose reads as "nothing to pick here", not as
   a control that failed to load. */
.region-picker select:disabled { background: #f4f6f9; color: var(--muted); }
/* A stored value the picker could not place. Shown rather than guessed at, and
   shown as information rather than as an error -- nothing is wrong with it. */
.region-note { margin: 6px 0 0; color: var(--warn-ink); }

@media (max-width: 720px) {
  .region-picker { grid-template-columns: 1fr; }
}

/* ---- 사업 핵심정보, on every card ---- */
/* A label column and a value column, so 지원금액 / 지원지역 / 지원대상 line up down
   the card and a reader can run their eye along one of them. Narrow label track:
   every label here is three or four characters. */
.result-facts {
  margin: 8px 0 0;
  display: grid;
  grid-template-columns: 64px minmax(0, 1fr);
  gap: 2px 10px;
  font-size: 13px;
}
.fact { display: contents; }
.fact-k { color: var(--muted); white-space: nowrap; }
.fact-v { color: var(--ink); min-width: 0; overflow-wrap: anywhere; }
/* 확인 필요 is an answer, not a value: it reads as the muted label does so it is
   not mistaken for a figure. */
.fact-v:empty { display: none; }

@media (max-width: 560px) {
  .result-facts { grid-template-columns: 58px minmax(0, 1fr); font-size: 12.5px; }
}

/* ---- 사업계획서, the last block in a programme detail dialog ---- */
/* Sits under 참여자격 for the same reason the button exists: a member who has
   just read "참여 가능" is looking for what to do next. Reuses .badge and
   button.primary, so this block cannot drift away from the rest of the dialog. */
.program-proposal .pp-note { font-size: 14px; color: var(--ink); }
.program-proposal .pp-hint {
  margin: 4px 0 0; font-size: 12.5px; color: var(--muted); white-space: normal;
}

.pp-form { margin: 14px 0 0; display: grid; gap: 14px; }
.pp-field { display: grid; gap: 5px; }
.pp-field label { font-size: 13px; font-weight: 600; color: var(--ink); }
.pp-field input[type="text"], .pp-field textarea {
  width: 100%; box-sizing: border-box; padding: 9px 10px; font: inherit;
  font-size: 14px; color: var(--ink); background: #fff;
  border: 1px solid var(--line); border-radius: 6px;
}
.pp-field textarea { resize: vertical; min-height: 68px; line-height: 1.5; }
.pp-field input[type="text"]:focus-visible, .pp-field textarea:focus-visible {
  outline: 2px solid var(--brand); outline-offset: 0; border-color: var(--brand);
}
.pp-required { color: var(--bad-ink); margin-left: 3px; }

/* Absent company facts. Information, not an error: nothing is wrong with a
   company that has no recorded patents, and the draft simply stays quiet. */
.pp-unknown {
  margin: 16px 0 0; padding: 10px 12px; border-radius: 6px;
  background: #f4f6f9; font-size: 13px;
}
.pp-unknown h5 { margin: 0 0 4px; font-size: 13px; }
.pp-unknown p { margin: 0; font-size: 13px; color: var(--muted); }
.pp-unknown-list { margin-top: 6px !important; color: var(--ink) !important; }

.pp-actions {
  margin: 16px 0 0; display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
}
.pp-status { font-size: 13px; color: var(--muted); }
.pp-status.pp-error { color: var(--bad-ink); }

/* The result. Drafted prose reads as prose; the fact check reads as a notice
   about it, which is why it sits above the sections rather than after them. */
.pp-factcheck {
  margin: 14px 0 0; padding: 12px; border-radius: 6px;
  background: #f4f6f9; font-size: 13px;
}
.pp-factcheck h5 { margin: 0 0 6px; font-size: 13px; }
.pp-factcheck p { margin: 0 0 6px; font-size: 13px; }
.pp-warn { color: var(--bad-ink); font-weight: 600; }
.pp-claims { margin: 6px 0; padding-left: 18px; }
.pp-claims li { margin: 3px 0; font-size: 13px; }
.pp-claim-text { font-weight: 600; color: var(--bad-ink); }
.pp-claim-where { color: var(--muted); margin-left: 6px; }

.pp-sections { margin: 16px 0 0; display: grid; gap: 16px; }
.pp-section h5 { margin: 0 0 6px; font-size: 14px; color: var(--brand-dark); }
.pp-section p {
  margin: 0 0 8px; font-size: 14px; line-height: 1.65; white-space: pre-wrap;
}
.pp-meta { margin: 14px 0 0; font-size: 12.5px; color: var(--muted); }

/* The count that replaces the repeats. A condition found in seventeen parsed
   copies of the same attachment is one condition; this says so quietly rather
   than printing the line seventeen times. */
.cond-repeat { color: var(--muted); font-size: 12px; white-space: nowrap; }

/* ---- 사업계획서에 사용할 정보 (출처 확인) ---- */
/* Three counts were not checkable. These groups hold the facts themselves, so
   a member can read what the draft will be written from and follow each one
   back before anything is generated. Native <details>, so opening a group
   needs no script and keeps working if one fails to load. */
.pp-sources { margin: 14px 0 0; }
.pp-sources h5 { margin: 0 0 8px; font-size: 13px; }
.pp-group {
  border: 1px solid var(--line); border-radius: 6px;
  margin-bottom: 6px; background: #fff;
}
.pp-group > summary {
  cursor: pointer; padding: 9px 12px; font-size: 13px; font-weight: 600;
  color: var(--ink); list-style: none;
}
.pp-group > summary::-webkit-details-marker { display: none; }
.pp-group > summary::before { content: "▸ "; color: var(--muted); }
.pp-group[open] > summary::before { content: "▾ "; }
.pp-group > summary:focus-visible { outline: 2px solid var(--brand); }
.pp-open { color: var(--brand); font-weight: 500; margin-left: 4px; }

.pp-facts { padding: 0 12px 10px; display: grid; gap: 10px; }
.pp-fact { padding-top: 10px; border-top: 1px solid var(--line); }
.pp-fact:first-child { border-top: 0; padding-top: 0; }
.pp-fact-label { font-size: 12.5px; font-weight: 600; color: var(--muted); }
.pp-fact-value {
  margin: 2px 0 0; font-size: 14px; color: var(--ink); line-height: 1.55;
  overflow-wrap: anywhere; white-space: pre-wrap;
}
.pp-fact-basis, .pp-fact-src { margin: 3px 0 0; font-size: 12px; color: var(--muted); }
.pp-fact-src a { color: var(--brand); }

/* A fact that did not check out says so where it is read, not in a footnote. */
.pp-status-note { font-weight: 600; font-size: 11.5px; }
.pp-status-note.bad { color: var(--bad-ink); }
.pp-status-note.warn { color: var(--warn-ink); }
.pp-status-note.muted { color: var(--muted); font-weight: 500; }

/* ---- 확인이 필요한 항목 ---- */
/* One question per company fact, with the notice's own wording under it so a
   member can see what they are answering about. */
.pp-choices { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 4px; }
.pp-choice {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 7px 11px; border: 1px solid var(--line); border-radius: 6px;
  font-size: 13px; cursor: pointer; background: #fff;
}
.pp-choice:has(input:checked) { border-color: var(--brand); background: #eaf2fd; }
.pp-choice input { margin: 0; }
.pp-review-form { margin: 12px 0 0; display: grid; gap: 14px; }

/* ---- 참여자격 자동 판정 배너 (초안 작성 화면) ---- */
/* The verdict follows the member into the form. It is a warning, not a gate:
   the draft is theirs to write and the notice is theirs to read. */
.pp-verdict {
  margin: 0 0 12px; padding: 9px 12px; border-radius: 6px;
  font-size: 13px; font-weight: 600;
}
.pp-verdict.warn { background: var(--warn-bg); color: var(--warn-ink); }
.pp-verdict.bad { background: var(--bad-bg); color: var(--bad-ink); }

/* ---- 초안 입력정보의 역할 구분 ---- */
/* Three groups doing three jobs. Under one heading they looked
   interchangeable, and a notice's funded fields are not a company's work. */
.pp-role {
  margin: 12px 0 5px; font-size: 12.5px; font-weight: 700; color: var(--ink);
}
.pp-role:first-of-type { margin-top: 4px; }

/* 기존 작성본 (PHASE 5-J)
   Offered above the 작성 button, never in place of it: the same notice funds
   more than one idea, and a member who already wrote one proposal for it is
   not thereby finished with it. */
.pp-existing {
  margin: 10px 0; padding: 10px 12px; border: 1px solid var(--line);
  border-radius: 8px; background: var(--paper-2, #fafafa);
}
.pp-existing > p { margin: 0 0 8px; font-size: 13px; }
.pp-existing-list { list-style: none; margin: 0; padding: 0; }
.pp-existing-item {
  display: flex; flex-wrap: wrap; gap: 8px; align-items: baseline;
  padding: 6px 0; border-top: 1px solid var(--line);
}
.pp-existing-item:first-child { border-top: 0; }
.pp-existing-title { font-weight: 600; font-size: 13px; }
.pp-existing-meta { font-size: 12px; color: var(--muted); }
.pp-continue { margin-left: auto; font-size: 12px; }
.pp-archive { font-size: 13px; }

/* 사업계획서 보관함 (/proposals) */
.pl-list { margin-top: 12px; }
.pl-item {
  padding: 12px 0; border-top: 1px solid var(--line);
}
.pl-item:first-child { border-top: 0; }
.pl-title { margin: 0 0 2px; font-size: 15px; }
.pl-program { margin: 0 0 2px; font-size: 13px; color: var(--ink); }
.pl-meta { margin: 0 0 8px; font-size: 12px; color: var(--muted); }
.pl-actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.pl-versions {
  display: flex; flex-wrap: wrap; gap: 6px; align-items: center;
  margin: 10px 0;
}
.pl-version {
  font-size: 12px; padding: 3px 9px; border: 1px solid var(--line);
  border-radius: 999px; background: #fff; cursor: pointer;
}
.pl-version.active { border-color: var(--accent, #2f6f4f); font-weight: 700; }
.pl-block { margin: 16px 0; }
.pl-block h4 { margin: 0 0 6px; font-size: 14px; }
.pl-inputs { margin: 8px 0 0; }
.pl-input {
  display: flex; gap: 10px; padding: 5px 0; border-top: 1px dotted var(--line);
}
.pl-input dt { flex: 0 0 140px; font-size: 12.5px; color: var(--muted); }
.pl-input dd { margin: 0; font-size: 13px; white-space: pre-wrap; }
.pl-missing { color: var(--muted); }
.pl-section { margin: 10px 0; }
.pl-section h5 { margin: 0 0 4px; font-size: 13.5px; }
.pl-claims { margin: 6px 0 0; padding-left: 18px; font-size: 12.5px; }
.pl-claim-where { color: var(--muted); margin-left: 6px; }

/* 통합검색 (PHASE 5-K)
   The box sits in the header on every page; the results are one list with a
   kind on each row, because a member looking for "드론" does not know in
   advance whether the answer is a notice, their own plan, or something
   somebody said in 보리수 톡. */
/* Row two: one field, centred on the window.

   It used to be a fourth child of `.header-row`, which silently turned that
   grid's `auto 1fr auto` into `auto 1fr auto auto`: the nav stopped being
   centred on the screen and the member controls were pushed to the edge.
   Search is not navigation either -- it acts on the whole site rather than
   going to one part of it -- so it gets the row it needs and the nav gets its
   three tracks back. */
.header-search-row {
  display: flex; justify-content: center; align-items: center;
  padding: 8px 24px 0;
}
.header-search { display: flex; gap: 6px; align-items: center; }
.header-search input {
  /* Wide enough to read a typed sentence back, not so wide that it becomes
     the page. */
  width: 440px; max-width: 72vw; padding: 7px 14px; font-size: 13.5px;
  border: 1px solid var(--line); border-radius: 999px;
}
.header-search button {
  border: 0; background: none; cursor: pointer; font-size: 17px; padding: 2px 4px;
  color: #fff; line-height: 1;
}
.sr-only {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap;
}
.sr-form { display: flex; gap: 8px; margin: 12px 0; }
.sr-form input { flex: 1; padding: 8px 12px; font-size: 14px;
  border: 1px solid var(--line); border-radius: 6px; }
.sr-summary { margin: 10px 0 6px; font-size: 13px; color: var(--muted); }
.sr-tabs, .sr-filters {
  display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin: 8px 0;
}
.sr-tab, .sr-filter {
  font-size: 12.5px; padding: 4px 10px; border: 1px solid var(--line);
  border-radius: 999px; background: #fff; cursor: pointer;
}
.sr-tab.active, .sr-filter.active {
  border-color: var(--accent, #2f6f4f); font-weight: 700;
}
.sr-count { color: var(--muted); font-weight: 400; }
.sr-agency { font-size: 12.5px; padding: 4px 9px; border: 1px solid var(--line);
  border-radius: 999px; width: 130px; }
.sr-item { padding: 12px 0; border-top: 1px solid var(--line); }
.sr-item:first-child { border-top: 0; }
.sr-kind { margin: 0 0 2px; font-size: 11.5px; color: var(--muted); }
.sr-title { margin: 0 0 3px; font-size: 15px; }
.sr-meta { margin: 0 0 4px; font-size: 12px; color: var(--muted); }
.sr-snippet { margin: 0; font-size: 13px; }
@media (max-width: 560px) { .header-search input { width: 100%; max-width: none; } }

/* 모집 종료 안내 (PHASE 5-M)
   A notice, not a gate. Said in the proposal block because that is where
   somebody is about to spend three minutes of a 32B server on a plan for a
   round that has already closed -- and because next year's round is a perfectly
   good reason to write one anyway. */
.pp-period-closed {
  margin: 0 0 10px; padding: 9px 12px; border-radius: 8px;
  border: 1px solid var(--line); background: var(--paper-2, #fafafa);
}
.pp-period-closed p { margin: 0 0 4px; font-size: 13px; }
.pp-period-closed p:last-child { margin-bottom: 0; color: var(--muted); }

/* 지원사업 검색 필터 행 (PHASE 5-N)
   Three named rows rather than one long wrap: 지원목적, 마감일 and 정렬 answer
   different questions, and a member scanning for 모집 종료 should not have to
   read past ten purposes to find it. Same pill shape as the tabs above, so the
   row reads as part of the same control. */
.sr-filter-row {
  display: flex; flex-wrap: wrap; gap: 6px; align-items: center;
  margin: 0 0 6px;
}
.sr-filter-name {
  flex: 0 0 62px; font-size: 12px; color: var(--muted); font-weight: 700;
}
@media (max-width: 560px) { .sr-filter-name { flex-basis: 100%; } }

/* 공지사항 ticker (PHASE 5-P)
   One line, at the top of the home page's own content, between the search box
   in the header and what the page is for.

   The track holds the run twice and translates by exactly -50%, which is one
   full copy: the second lap starts where the first ended, so the line never
   shows a gap or snaps back. The duplicate is aria-hidden and untabbable --
   a screen reader and the tab key should meet each notice once.

   Fixed height on the window, so a long title cannot make the page taller, and
   `overflow: hidden` on it rather than on the track, so the moving part is
   free to be wider than the screen. */
.notice-ticker {
  display: flex; align-items: center; gap: 12px;
  margin: 0 0 14px; padding: 8px 14px;
  border: 1px solid var(--line); border-radius: 8px;
  background: var(--paper-2, #fafafa);
}
.notice-ticker-head {
  flex: 0 0 auto; font-size: 13px; font-weight: 700;
  color: var(--ink); text-decoration: none; white-space: nowrap;
}
.notice-ticker-head:hover { text-decoration: underline; }
.notice-ticker-window {
  position: relative; flex: 1 1 auto; min-width: 0;
  height: 22px; overflow: hidden;
}
.notice-ticker-track {
  display: flex; width: max-content; height: 22px; align-items: center;
  animation: notice-ticker-run 40s linear infinite;
}
.notice-ticker-run { display: flex; align-items: center; gap: 24px; padding-right: 24px; }
.notice-ticker-item {
  font-size: 13px; color: var(--ink); text-decoration: none; white-space: nowrap;
}
.notice-ticker-item:hover { text-decoration: underline; }
.notice-ticker-item.important { font-weight: 700; }
.notice-ticker-tag { color: var(--muted); margin-right: 4px; }

@keyframes notice-ticker-run {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}

/* Stop while somebody is reading it or tabbing through it: a line that moves
   out from under the pointer is a line you cannot click. */
.notice-ticker:hover .notice-ticker-track,
.notice-ticker:focus-within .notice-ticker-track {
  animation-play-state: paused;
}

/* Motion is the decoration, not the content. Without it the row becomes a
   strip that can be scrolled by hand, and every notice is still reachable. */
@media (prefers-reduced-motion: reduce) {
  .notice-ticker-track { animation: none; }
  .notice-ticker-window { overflow-x: auto; }
  .notice-ticker-run + .notice-ticker-run { display: none; }
}

/* 공지사항 목록·상세 */
.notice-list { margin-top: 10px; }
/* 목록: one line per notice, like a board -- 번호 · 분류 · 제목 · 등록일. */
.notice-rows { list-style: none; margin: 0; padding: 0; border-top: 2px solid var(--ink, #1f2937); }
.notice-row {
  display: grid; grid-template-columns: 56px 64px minmax(0, 1fr) 88px;
  align-items: center; gap: 10px; padding: 11px 6px;
  border-bottom: 1px solid var(--line); font-size: 14px;
}
.notice-row-head { font-size: 12.5px; font-weight: 600; color: var(--muted); padding: 8px 6px; }
.notice-row-head .notice-subject { text-align: center; }
.notice-no, .notice-date { text-align: center; color: var(--muted); font-size: 13px; }
.notice-cat { text-align: center; color: var(--muted); font-size: 12.5px; white-space: nowrap; }
.notice-subject {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  color: var(--ink); text-decoration: none;
}
a.notice-subject:hover { color: var(--brand); text-decoration: underline; }
.notice-row.is-important { background: #fff8f6; }
.notice-row.is-important .notice-subject { font-weight: 600; }
.notice-row .notice-important { margin-right: 0; }
@media (max-width: 560px) {
  .notice-row { grid-template-columns: 44px minmax(0, 1fr) 76px; gap: 8px; }
  .notice-cat { display: none; }
}
.notice-title { margin: 0 0 3px; font-size: 15px; }
.notice-meta { margin: 0; font-size: 12px; color: var(--muted); }
.notice-summary { margin: 10px 0 0; font-size: 13px; color: var(--muted); }
.notice-important {
  display: inline-block; font-size: 11.5px; font-weight: 700;
  color: var(--bad-ink, #b42318); margin-right: 5px;
}
.notice-body { margin-top: 12px; font-size: 14px; line-height: 1.7; }
.notice-body p { margin: 0 0 12px; }

/* 관리자 공지 화면 */
.notice-table td { vertical-align: top; font-size: 13px; }
.notice-row-actions { display: flex; flex-wrap: wrap; gap: 6px; }
.notice-row-actions button, .notice-row-actions a { font-size: 12px; }
.notice-form { display: flex; flex-direction: column; gap: 10px; margin-top: 10px; }
.notice-form label { display: flex; flex-direction: column; gap: 4px; font-size: 13px; }
.notice-form-check { flex-direction: row !important; align-items: center; gap: 6px; }
.notice-form input, .notice-form select, .notice-form textarea {
  padding: 7px 10px; font-size: 13.5px;
  border: 1px solid var(--line); border-radius: 6px;
}

/* Programme source beside a title and in the notice dialog (static/source_tag.js). */
.source-tag { font-size: 0.85em; font-weight: 600; color: #1d5fa8; margin-right: 2px; }
.detail-source .source-name { font-weight: 600; color: #1d5fa8; margin-right: 10px; }
.source-stats { margin: 6px 0 10px; padding: 8px 10px; border: 1px dashed #9db8d9; border-radius: 6px; background: #f5f9ff; font-size: 0.85em; color: #24476e; }
.source-stats-title { font-weight: 700; margin-bottom: 2px; }
.source-stats-sites { color: #4a6688; }
.source-stats-warn { margin: 4px 0 0; color: #a3480f; font-weight: 600; }

/* 지원사업 상세 page (/programs/{id}) and the links that open it. */
.result-title a { color: inherit; text-decoration: none; }
.result-title a:hover { text-decoration: underline; }
.program-back { margin: 0 0 10px; font-size: 14px; }
.program-detail .program-title { margin: 10px 0 6px; font-size: 22px; line-height: 1.4; overflow-wrap: anywhere; }
.program-cta { margin: 14px 0 0; }
.program-facts { margin: 0; display: grid; gap: 6px; }
.program-fact { display: grid; grid-template-columns: 110px 1fr; gap: 10px; font-size: 14px; }
.program-fact dt { color: var(--muted); }
.program-fact dd { margin: 0; overflow-wrap: anywhere; white-space: pre-wrap; }
.program-files { margin: 0; padding-left: 18px; font-size: 14px; }
.program-files li { margin: 0 0 6px; overflow-wrap: anywhere; }
.program-file-meta { color: var(--muted); margin-left: 6px; font-size: 12px; }
.detail-source a, .detail-apply a { overflow-wrap: anywhere; }
.program-notice { margin: 22px 0 0; font-size: 12px; color: var(--muted); }
.program-community:empty { display: none; }
@media (max-width: 560px) {
  .program-fact { grid-template-columns: 1fr; gap: 2px; }
  .program-detail .program-title { font-size: 19px; }
}

/* 추천 근거, unfolded under a recommendation card instead of a dialog. */
.rec-reasons { margin-top: 12px; padding: 4px 12px 12px; border-left: 3px solid var(--line); }

/* 지원사업 탐색: 지역 (시·도, then 시·군·구). */
.explorer-region { display: flex; flex-wrap: wrap; gap: 8px; }
.explorer-region select { min-width: 150px; }
.region-hint { margin: 6px 0 0; font-size: 12.5px; }

/* ======================================================================
   Compact lists and filters (맞춤 지원사업 추천 · 지원사업 탐색 · 검색)
   ----------------------------------------------------------------------
   These screens are for scanning titles and picking one, so a notice takes
   two or three short lines, not a card, and each filter is one line --
   label on the left, buttons on the right -- so the list starts on the
   first screen. Declared last so it overrides the roomier rules above.
   ====================================================================== */

/* -- filter blocks: one line per filter ------------------------------ */
.filter-card { padding: 12px 16px; margin-bottom: 12px; }
.filter-card-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 12px; }
.filter-card-head h2 { margin: 0; font-size: 17px; }
.filter-card-head .hint { margin: 0; font-size: 12px; }
.filter-grid, .rec-filters, .sort-grid {
  display: grid; grid-template-columns: 64px minmax(0, 1fr);
  gap: 5px 10px; align-items: start; margin: 8px 0 0;
}
.filter-grid > .filter-heading, .rec-filters > .filter-heading,
.sort-grid > .filter-heading,
.rec-filters .filter-heading:first-child {
  margin: 0; padding: 4px 0 0; border: 0;
  font-size: 12.5px; font-weight: 600; color: var(--muted); white-space: nowrap;
}
.preset-row { gap: 4px; }
.preset.filter { font-size: 12.5px; padding: 3px 10px; line-height: 1.5; }
.explore-modes { margin: 4px 0 0; }
.explorer-region { gap: 6px; align-items: center; }
.explorer-region select, .explorer-options select { padding: 3px 6px; font-size: 12.5px; min-width: 0; }
.region-hint { font-size: 12px; color: var(--muted); }
.explorer-options { margin: 0; gap: 14px; }
.explorer-options label.inline { font-size: 12.5px; gap: 6px; }
.mode-hint { margin: 2px 0 6px; font-size: 12px; }
.gate-summary { margin: 6px 0 6px; font-size: 12.5px; }
.results-toolbar { margin: 6px 0; }

/* -- the list itself --------------------------------------------------- */
.result {
  padding: 7px 12px; margin-bottom: 5px; border-radius: 6px;
}
.result-title { font-size: 14.5px; line-height: 1.4; }
.result .badge-row { margin: 3px 0 0; gap: 4px; }
.result .badge { font-size: 11.5px; padding: 1px 7px; }
/* 핵심정보 as one wrapped line of "label value" pairs instead of a table. */
.result .result-facts {
  display: flex; flex-wrap: wrap; gap: 0 12px; margin: 3px 0 0; font-size: 12.5px;
}
.result .fact { display: inline-flex; gap: 4px; min-width: 0; max-width: 100%; }
.result .fact-v { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 26em; }
/* Agency and period side by side; reasons and summaries on one line. */
.result .result-agency, .result .result-period {
  display: inline-block; margin: 3px 12px 0 0; font-size: 12.5px;
}
.result .result-why, .result .result-note {
  margin: 3px 0 0; font-size: 12.5px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* 좋아요·댓글 and the buttons on one line. */
.result .engagement { display: inline-flex; margin: 4px 12px 0 0; vertical-align: middle; }
.result .result-actions { display: inline-flex; margin: 4px 0 0; gap: 8px; vertical-align: middle; }
.result .link-btn { padding: 2px 9px; font-size: 12px; }
.pager, .pagination { margin-top: 10px; }

/* -- 검색 ----------------------------------------------------------------- */
.sr-summary-row { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 6px 12px; margin: 6px 0 4px; }
.sr-summary-row .sr-summary { margin: 0; }
.sr-size select { padding: 2px 6px; font-size: 12.5px; }
.sr-tabs, .sr-filters { margin: 4px 0; gap: 4px; }
.sr-filter-row { margin: 0 0 3px; }
.sr-tab, .sr-filter { padding: 2px 9px; font-size: 12px; }
.sr-item { padding: 6px 0; }
.sr-kind { display: inline; margin: 0 6px 0 0; }
.sr-title { display: inline; margin: 0; font-size: 14.5px; }
.sr-meta { margin: 2px 0 0; }
.sr-snippet {
  margin: 1px 0 0; font-size: 12.5px; color: var(--muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

@media (max-width: 560px) {
  .filter-grid, .rec-filters, .sort-grid { grid-template-columns: 1fr; gap: 2px; }
  .filter-grid > .filter-heading, .rec-filters > .filter-heading, .sort-grid > .filter-heading { padding: 4px 0 0; }
  .result .fact-v { max-width: 16em; }
}

/* ======================================================================
   One-line rows (지원사업 탐색 · 빠른/정밀 추천 · 좋아요순/댓글순)
   ----------------------------------------------------------------------
   [출처] [구분] [마감/D-Day] 공고명 | 기관 | 지역 -- one line per notice,
   drawn by window.borysuListRow (source_tag.js). The rows sit in one bordered
   box separated by hairlines, like a board, rather than as stacked cards. The
   title takes whatever width is left and is cut with "…"; the full title is
   its tooltip and its page. About 40px a row on a desktop. Below 640px a row
   is at most two lines -- 출처·마감·제목, then 기관 | 지역 -- and below 420px
   the 지원목적 / 관련도 badge steps aside for the title.
   ====================================================================== */
#results:has(.result-row), #explorer-results:has(.result-row) {
  border: 1px solid var(--line); border-radius: 8px; background: #fff; overflow: hidden;
}
.result.result-row {
  border: 0; border-bottom: 1px solid var(--line); border-radius: 0;
  margin: 0; padding: 9px 12px; background: #fff;
}
.result.result-row:last-child { border-bottom: 0; }
.result.result-row:hover { background: #f6f9fd; }
.result.result-row.is-closed { background: #fafbfc; }
.row-line {
  display: flex; align-items: center; gap: 8px; min-width: 0; cursor: pointer;
  white-space: nowrap; font-size: 13.5px; line-height: 1.5;
}
.row-tags { display: inline-flex; align-items: center; gap: 4px; flex: 0 0 auto; }
.row-tags .source-tag { margin: 0; font-size: 12px; }
.result-row .badge { font-size: 11px; padding: 1px 6px; }
.row-title {
  flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis;
  font-weight: 600; color: var(--ink); text-decoration: none;
}
.row-title:hover { color: var(--brand); text-decoration: underline; }
.result-row.is-closed .row-title { color: var(--muted); }
.row-meta {
  flex: 0 1 auto; max-width: 46%; min-width: 0; display: inline-flex; align-items: center;
  color: var(--muted); font-size: 12.5px;
}
.row-meta > span { overflow: hidden; text-overflow: ellipsis; }
.row-meta > span + span::before { content: "|"; color: var(--line); margin: 0 6px; }
.row-agency { min-width: 3em; max-width: 16em; flex: 0 1 auto; }
.row-period { flex: 0 0 auto; }
.row-region { flex: 0 0 auto; max-width: 9em; }
.row-counts { flex: 0 0 auto; display: inline-flex; gap: 8px; color: var(--muted); font-size: 12px; }
.row-tail { flex: 0 0 auto; }
.row-tail .link-btn { padding: 1px 8px; font-size: 12px; }
.result-row .rec-reasons { margin: 6px 0 4px; white-space: normal; }

/* Under 1100px the dates give way to the title; the D-day badge stays. */
@media (max-width: 1100px) {
  .row-period { display: none; }
}
@media (max-width: 640px) {
  .result.result-row { padding: 7px 10px; }
  .row-line { flex-wrap: wrap; row-gap: 2px; }
  /* Line one: tags and title. Line two: 기관 | 지역, the counts and the
     button -- the meta box is wider than what line one leaves, so it wraps. */
  .row-title { flex: 1 1 40%; }
  .row-meta { flex: 1 1 60%; max-width: none; }
}
@media (max-width: 420px) {
  .row-tags .badge.purpose, .row-tags .badge-relevance, .row-tags .badge-fast { display: none; }
}
