
.gb-breadcrumb { margin: 0 0 var(--board-space-2); font-size: var(--board-size-meta); color: var(--board-dim); }
.gb-page-title { border-bottom: 1px solid var(--board-ink); padding-bottom: var(--board-space-2); }
.gb-notice, .gb-error, .gb-hint { font-size: var(--board-size-meta); }
.gb-notice { padding: var(--board-space-3) var(--board-space-4); border-radius: var(--board-radius); background: var(--board-tint); color: var(--board-mid); }
.gb-error { color: var(--board-danger); }
.gb-hint { color: var(--board-dim); }
.gb-empty { color: var(--board-dim); }
.gb-tags { display: flex; flex-wrap: wrap; gap: var(--board-space-1); margin: var(--board-space-2) 0; }
/* A tag is a chip, not a bordered button: production's accent tint under the accent text, no border, at the caption size. The ground is --board-notice, the one accent tint the sheet keeps (production's rgb(237, 248, 245)); the pad is a literal off the spacing scale. */
.gb-tag { display: inline-flex; align-items: center; min-height: 1.8125rem; padding: 0.25rem 0.55rem; border: 0; border-radius: 999px; background: var(--board-notice); font-size: var(--board-size-caption); color: var(--board-accent-text); }
.gb-tag:hover { text-decoration: none; background: color-mix(in srgb, var(--board-accent) 14%, var(--board-surface)); }

/*
 * Core's two-line list, where Core still draws one: a member's posts, the profile's bookmarks.
 * The meta is one dot-separated run — flex collapses the leading space Core writes before the
 * author, so the parts read as "board · author · date" rather than run together (board-skin.md §9).
 */
ol.gb-content-list { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--board-line); }
ol.gb-content-list > li { padding: var(--board-space-3) 0; border-bottom: 1px solid var(--board-line); }
ol.gb-content-list > li > a { font-weight: var(--board-weight-bold); font-size: var(--board-size-ui); }
ol.gb-content-list small { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 var(--board-space-2); color: var(--board-dim); font-size: var(--board-size-meta); }
ol.gb-content-list small > * + *::before { content: "·"; color: var(--board-dim); margin-right: var(--board-space-2); }
ol.gb-content-list .gb-row-metrics { float: right; color: var(--board-dim); font-size: var(--board-size-meta); margin-left: var(--board-space-2); }
ol.gb-content-list .gb-summary { margin: var(--board-space-1) 0 0; color: var(--board-dim); font-size: var(--board-size-meta); }
ol.gb-content-list .gb-space { color: var(--board-mid); }

.gb-widget .gb-content-list { border-top: 0; }
.gb-widget h2 { font-size: var(--board-size-head); font-weight: var(--board-weight-title); margin: 0 0 var(--board-space-3); }
.gb-widget .gb-more { text-align: right; font-size: var(--board-size-meta); margin: var(--board-space-1) 0 0; }

.gb-actions { display: flex; flex-wrap: wrap; gap: var(--board-space-2); align-items: center; margin: var(--board-space-2) 0; }
.gb-actions .gb-write { font-weight: var(--board-weight-bold); }
.gb-engagement { display: flex; flex-wrap: wrap; gap: var(--board-space-4); align-items: center; margin: var(--board-space-4) 0; }
.gb-reactions { display: inline-flex; gap: var(--board-space-2); align-items: center; }
.gb-reaction { display: inline-flex; align-items: center; gap: var(--board-space-1); }
.gb-reaction[aria-pressed="true"] { border-color: var(--board-ink); }
.gb-bookmark-active { font-weight: var(--board-weight-bold); }
.gb-more-menu summary { display: inline-block; padding: var(--board-space-1) var(--board-space-2); border: 1px solid var(--board-rule); border-radius: var(--board-radius); font-weight: var(--board-weight-bold); color: var(--board-mid); }
.gb-more-menu summary::after { content: none; }
.gb-more-menu .gb-menu-list { left: 0; right: auto; }
/* The tools row sits flush right, so its list must anchor to the summary right edge and open leftward. */
.post-tools .gb-more-menu .gb-menu-list { left: auto; right: 0; }
/* On phones a nested comment summary is pushed right by the indent, so a list opening rightward falls off the window; anchor it to the summary right edge and open leftward. */
@media (max-width: 35rem) { .gb-comment .gb-more-menu .gb-menu-list { left: auto; right: 0; } }
/*
 * The ellipsis is a standalone icon control, not an inline text-link exception. Keep production's
 * compact visual glyph, but on touch hardware give the summary the 44px hit area a thumb needs.
 */
@media (pointer: coarse) {
  .gb-more-menu > summary {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 2.75rem;
    min-height: 2.75rem;
  }
}
.gb-more-menu .gb-menu-list form { display: block; padding: var(--board-space-2) var(--board-space-4); }
.gb-more-menu .gb-menu-list form button { padding: 0; width: auto; }
.gb-moderation, .gb-report-form { font-size: var(--board-size-meta); }
.gb-attachments { list-style: none; margin: var(--board-space-3) 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--board-space-2); }
/* A block, not the inline default: an inline picture sits on the text baseline and the row's gap grows by the line's descender (15px drawn for an 8px gap). */
.gb-attachment-image img { display: block; max-width: 100%; height: auto; border-radius: var(--board-radius-s); }
.gb-attachment-video video { display: block; max-width: 100%; height: auto; border-radius: var(--board-radius-s); }
/* A provider frame (content-blocks.md §3b): the body's iframe rule caps its width (styles-pages.ts); its height is the runtime's, from the frame's own message. */
.gb-embed-frame { margin: var(--board-space-3) 0; }
.gb-embed-source { margin: var(--board-space-1) 0 0; font-size: var(--board-size-meta); }
.gb-image-source { text-align: center; }
.gb-source-ref { font-size: .75em; line-height: 0; vertical-align: super; }
.gb-body .gb-source-ref a { text-decoration: none; }
.gb-source-notes { margin-top: var(--board-space-4); padding-top: var(--board-space-2); border-top: 1px solid var(--board-line); font-size: var(--board-size-meta); }
.gb-source-notes ol { margin-bottom: 0; padding-left: 1.4rem; }
.gb-source-notes-title { margin: 0 0 var(--board-space-1); font-size: var(--board-size-meta); font-weight: var(--board-weight-bold); }
.gb-source-notes li:target { background: var(--board-tint); }
.gb-attachment-file a { display: inline-block; padding: var(--board-space-1) var(--board-space-3); border: 1px solid var(--board-rule); border-radius: var(--board-radius); font-size: var(--board-size-meta); }
/* A post's pictures over the text (`settings.postImages: above`, board-skin.md §9): production's own 8px gap wrapping 24px over the body, off the skin's scale, so literals, and its softer 12px corner on the images in that row. */
.post-images .gb-attachments { margin: 0 0 1.5rem; gap: 0.5rem; }
.post-images .gb-attachment-image img { border-radius: 0.75rem; }

/*
 * Compose: the fields sit inside the skin's surface card — production's PostForm draws them in an
 * AppSurface (a 1.1rem pad, the hairline, the card radius and the small shadow) with the h1 on the
 * ground above it — so the pad here is the card pad the listing's cards already take, and on a
 * phone it steps in so the card's edges never push the fields past the window.
 */
.gb-content-form { display: flex; flex-direction: column; gap: var(--board-space-3); max-width: 52rem; padding: var(--board-card-pad); background: var(--board-surface); border: 1px solid var(--board-line); border-radius: var(--board-radius-l); box-shadow: var(--board-shadow); }
.gb-content-form label { display: flex; flex-direction: column; gap: var(--board-space-1); font-size: var(--board-size-meta); color: var(--board-mid); font-weight: var(--board-weight-bold); }
/* Core's pin control is a label whose first child is the box, not a label.gb-checkbox: it sits in a line the same way (theme.css matches it the same). */
.gb-content-form label.gb-checkbox, .gb-content-form label:has(> input[type="checkbox"]:first-child), .gb-checkbox { flex-direction: row; align-items: center; gap: var(--board-space-2); font-weight: var(--board-weight-normal); }
.gb-content-form input[type="text"], .gb-content-form select { width: 100%; }
/* A plugin's compose.fields arrive inside one slot wrapper, which is one child of the form's gap column; the wrapper parts its own fields by the same gap. */
.gb-content-form > .gb-slot-compose-fields { display: flex; flex-direction: column; gap: var(--board-space-3); }
.gb-content-form textarea, .gb-editor textarea { min-height: 18rem; font-family: var(--board-font); line-height: var(--board-leading-read); }
/* A toolbar tool is not a form control: it keeps the editor's own 36px height rather than the control height. */
.gb-editor-tool { min-height: 2.25rem; }
/* editor.css loads after this sheet with the same selectors, so each carries one class more to win. */
.gb-editor .gb-editor-toolbar, .gb-editor .gb-editor-host { border-color: var(--board-line); }
.gb-editor:focus-within .gb-editor-toolbar.gb-editor-toolbar, .gb-editor:focus-within .gb-editor-host.gb-editor-host { border-color: var(--board-focus-line, var(--board-accent)); }
/* 등록 at the row's right end with the quiet 취소 at its left, both the same height (production's .gb-actions); 삭제 stays off that row, a danger not to be sat beside the primary. */
.gb-content-form .gb-submit { display: flex; justify-content: flex-end; gap: var(--board-space-2); align-items: center; margin: var(--board-space-2) 0 0; }
.gb-content-form .gb-delete { margin-left: auto; }
.gb-content-form .gb-error { margin: 0; }
@media (max-width: 47.99rem) {
  .gb-content-form { padding: var(--board-space-4) var(--board-space-3); }
}

/* Profile, auth, settings, series, errors. */
/*
 * The profile head is one surface card: the picture (or its initial, when the member has none) beside
 * the name, the dim handle and the bio (board-skin.md §9). The grid puts the avatar in the first
 * column and the words in the second; the stats span the whole width under them.
 */
.gb-profile { display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: center; padding: var(--board-card-pad); background: var(--board-surface); border: 1px solid var(--board-line); border-radius: var(--board-radius-l); box-shadow: var(--board-shadow); }
.gb-profile h1 { grid-column: 2; margin: 0; }
.gb-profile .gb-identity, .gb-profile .gb-bio { grid-column: 2; }
.gb-profile .gb-avatar { grid-column: 1; grid-row: 1 / span 3; width: 4rem; height: 4rem; border-radius: 50%; object-fit: cover; margin-right: var(--board-space-4); }
.gb-profile .gb-avatar-initial { display: inline-flex; align-items: center; justify-content: center; background: var(--board-accent); color: var(--board-on-accent); font-size: var(--board-size-page); font-weight: var(--board-weight-bold); }
.gb-profile .gb-identity { display: flex; flex-wrap: wrap; align-items: center; gap: var(--board-space-2); margin: 2px 0 0; font-size: var(--board-size-meta); }
.gb-profile .handle { color: var(--board-dim); }
/* A bio is typed with its own line breaks; collapsing them runs the lines into one paragraph the writer did not write. */
.gb-profile .gb-bio { margin: var(--board-space-2) 0 0; color: var(--board-mid); white-space: pre-line; }
/* The stats read as one dot-separated line, not a bulleted list (board-skin.md §9). */
.gb-profile .gb-meta { grid-column: 1 / -1; display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 var(--board-space-5); overflow: hidden; list-style: none; margin: var(--board-space-3) 0 0; padding: 0; color: var(--board-dim); font-size: var(--board-size-meta); }
/* The dot is pulled left into the gap; a line's first item has its dot outside the list's overflow, so a wrapped phone line never opens with one (the .post-meta idiom, §2). */
.gb-profile .gb-meta > li::before { content: "·"; display: inline-block; width: var(--board-space-5); margin-left: calc(-1 * var(--board-space-5)); text-align: center; color: var(--board-dim); }
/* The posts list and the comments list sit on the same surface card the board list does; the last row drops its own hairline. */
.gb-profile-posts, .gb-profile-comments { padding: 0 var(--board-card-pad) var(--board-space-2); background: var(--board-surface); border: 1px solid var(--board-line); border-radius: var(--board-radius-l); box-shadow: var(--board-shadow); }
/* An empty list is a bare paragraph: with no row to bring its own padding, it gets the gap the card's foot already has (noriter-104). */
.gb-profile-posts > p, .gb-profile-comments > p { margin-top: calc(var(--board-space-2) + var(--board-space-3)); }
.gb-profile-posts .gb-content-list { border-top: 0; }
.gb-profile-posts .gb-content-list > li:last-child, .gb-profile-comments .gb-comment-list > li:last-child { border-bottom: 0; }
/* The author is the member whose page this is: the row already names them, so the meta drops it (board-skin.md §9). */
.gb-profile-posts .gb-content-list .gb-author { display: none; }
/* The heading repeats the active tab's label just above it: kept for a screen reader, out of sight (board-skin.md §9). */
.gb-profile-posts > h2, .gb-profile-comments > h2 { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }
.gb-profile-tabs { display: flex; gap: var(--board-space-4); margin: var(--board-space-4) 0; border-bottom: 1px solid var(--board-line); }
.gb-profile-tabs a, .gb-profile-tabs strong { padding: var(--board-space-2) 0 var(--board-space-2); margin-bottom: -1px; border-bottom: 2px solid transparent; font-size: var(--board-size-ui); font-weight: var(--board-weight-normal); color: var(--board-dim); }
.gb-profile-tabs a:hover { text-decoration: none; color: var(--board-text); }
.gb-profile-tabs a[aria-current], .gb-profile-tabs strong { color: var(--board-accent-text); font-weight: var(--board-weight-bold); border-bottom-color: var(--board-accent); }
.gb-profile-form, .gb-auth-form, .settings-form { display: flex; flex-direction: column; gap: var(--board-space-3); max-width: 30rem; }
.gb-profile-form label, .gb-auth-form label, .settings-form label { display: flex; flex-direction: column; gap: var(--board-space-1); font-size: var(--board-size-meta); color: var(--board-mid); font-weight: var(--board-weight-bold); }
.gb-profile-form input:not([type="checkbox"]), .gb-auth-form input:not([type="checkbox"]), .settings-form input:not([type="checkbox"]) { width: 100%; }
.gb-profile-danger { margin-top: var(--board-space-6); font-size: var(--board-size-meta); color: var(--board-dim); }
/* Sign in, sign up and the password forms are one centred surface card, so the fields are not a bare form on the ground (board-skin.md §2). */
/* The card's measures are production's sign-in dialog, off the skin's scale, so literals: 30rem wide on a 1.2rem pad, a 1.25rem title, 16px labels in the ink over 45px fields on the search controls' 0.4rem radius, and the way out — 비밀번호 찾기 under the button, 가입하기 or 로그인 under a hairline — in the accent. */
.gb-auth { max-width: 30rem; margin: var(--board-space-8) auto; padding: 1.2rem; background: var(--board-surface); border: 1px solid var(--board-line); border-radius: var(--board-radius-l); box-shadow: var(--board-shadow); }
.gb-auth h1 { margin: 0 0 var(--board-space-4); font-size: 1.25rem; font-weight: var(--board-weight-title); line-height: 1.35; }
.gb-auth .gb-auth-form { max-width: none; gap: var(--board-space-4); }
.gb-auth .gb-account-login-id { margin: 0 0 var(--board-space-5); }
.gb-auth .gb-auth-form label { gap: 0.3rem; font-size: 1rem; color: var(--board-text); }
.gb-auth .gb-auth-form label small { color: var(--board-dim); font-size: var(--board-size-caption); font-weight: 400; }
/* 로그인 유지 is a box and its sentence on one line, lighter than the field labels above it — the column rule above would stack the box over its words. */
.gb-auth .gb-auth-form label.gb-auth-remember { flex-direction: row; align-items: center; gap: 0.5rem; font-weight: var(--board-weight-normal); }
/* The recovery link and the way to join are two sentences: one to a line, centred under the hairline, never run together. */
.gb-auth .gb-auth-links { display: flex; flex-direction: column; align-items: center; gap: var(--board-space-2); }
.gb-auth .gb-auth-form > p:last-child { margin: 0; text-align: right; font-size: var(--board-size-meta); }
/* The profile edit form shares this card's measures: each row is a <p> holding a for-label and its control, so the row is the label's own gap column, and the checkbox rows sit in a line, lighter than the labels (me.vue:89's .gb-profile-form/.check). */
.gb-auth .gb-profile-form { max-width: none; gap: var(--board-space-4); }
/* p.gb-actions inherits .gb-actions' centre from the inherited sheet; stretch puts the submit back on the card's full width. */
.gb-auth .gb-profile-form > p { margin: 0; display: flex; flex-direction: column; align-items: stretch; gap: 0.3rem; }
.gb-auth .gb-profile-form label { gap: 0.3rem; font-size: 1rem; color: var(--board-text); }
.gb-auth .gb-profile-form label.gb-checkbox { flex-direction: row; align-items: center; gap: 0.5rem; font-weight: var(--board-weight-normal); }
.gb-auth .gb-profile-form label.gb-checkbox + label.gb-checkbox { margin-top: calc(var(--board-space-2) - 0.3rem); }
.gb-auth .gb-profile-form input[type="file"]::file-selector-button { margin-right: 0.6rem; padding: 0.45rem 0.8rem; border: 1px solid var(--board-line); border-radius: 0.4rem; background: var(--board-surface); color: var(--board-ink); font: inherit; }
.gb-auth .gb-profile-form input[type="file"]::file-selector-button:hover { background: var(--board-tint); }
.gb-auth .gb-profile-form .gb-profile-danger { margin: 0; text-align: center; }
.gb-auth .gb-profile-form .gb-profile-danger a.gb-danger { color: var(--board-danger); }
/*
 * The parts of one card (account settings' email, password, then the sign-in providers) split like production's
 * section + section — a hairline with air on both sides (me.vue:89). Whatever the part is: a section that follows
 * a form is a part like the form before it, and used to sit on that form's button (noriter-96).
 */
.gb-auth :is(.gb-auth-form, section) + :is(.gb-auth-form, section) { margin-top: var(--board-space-5); padding-top: var(--board-space-5); border-top: 1px solid var(--board-line); }
.gb-auth > p:last-child { margin: var(--board-space-5) 0 0; padding-top: var(--board-space-4); border-top: 1px solid var(--board-line); text-align: center; color: var(--board-dim); }
/* Underlined: inside a sentence, colour alone did not set the link apart (1.34:1 against the text, WCAG 1.4.1). */
.gb-auth p a { color: var(--board-accent-text); text-decoration: underline; text-underline-offset: 0.15em; }
/* The consent screen (auth.md §11): the client's address under its name, and what it asked for as a plain list. */
.gb-auth .gb-auth-consent-client { display: block; margin-top: var(--board-space-1); color: var(--board-dim); font-size: var(--board-size-caption); overflow-wrap: anywhere; }
.gb-auth .gb-auth-consent-scopes { margin: var(--board-space-4) 0 var(--board-space-5); font-size: var(--board-size-meta); }
.gb-auth .gb-auth-consent-scopes p { margin: 0 0 var(--board-space-1); color: var(--board-dim); }
.gb-auth .gb-auth-consent-scopes ul { margin: 0; padding-left: 1.1rem; }
.gb-providers { list-style: none; margin: var(--board-space-3) 0; padding: 0; display: flex; flex-direction: column; gap: var(--board-space-2); }
.gb-providers a, .gb-provider { display: block; padding: var(--board-space-2); border: 1px solid var(--board-rule); border-radius: var(--board-radius); text-align: center; font-weight: var(--board-weight-bold); }
.gb-providers a:hover { text-decoration: none; background: var(--board-tint); }
.gb-account-links { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; justify-content: center; gap: var(--board-space-1) var(--board-space-4); font-size: var(--board-size-meta); }
.gb-series-parts { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--board-line); counter-reset: part; }
.gb-series-parts > li { padding: var(--board-space-3) 0; border-bottom: 1px solid var(--board-line); counter-increment: part; }
.gb-series-parts > li::before { content: counter(part) ". "; color: var(--board-dim); }
/*
 * Notifications: one card of one-line rows — the same frame .gb-comments carries (surface, line,
 * radius, shadow) — the moment and a quiet 삭제 at the right. The shell's submit rule would
 * make every row's 삭제 the black button — a column of them over the list — so the row button's
 * selector is as strong as that one and comes after it, put back to dim text. 모두 읽음 is quiet
 * by name instead: the markup carries btn-quiet.
 */
.gb-notifications-read { display: flex; justify-content: flex-end; margin: 0 0 var(--board-space-2); }
.gb-notification-list { list-style: none; margin: 0; padding: 0 var(--board-card-pad); background: var(--board-surface); border: 1px solid var(--board-line); border-radius: var(--board-radius-l); box-shadow: var(--board-shadow); }
.gb-notifications > .gb-empty { margin: 0; padding: var(--board-space-8) var(--board-card-pad); background: var(--board-surface); border: 1px solid var(--board-line); border-radius: var(--board-radius-l); box-shadow: var(--board-shadow); text-align: center; }
/* The messaging plugin draws its own body div, not a notifications card: the same card face, its empty state on the shared empty pad. */
.messages-body { padding: var(--board-card-pad); background: var(--board-surface); border: 1px solid var(--board-line); border-radius: var(--board-radius-l); box-shadow: var(--board-shadow); }
.messages-body > .gb-empty { margin: 0; padding: var(--board-empty-pad); color: var(--board-dim); text-align: center; }
.gb-notification-list > li { display: flex; align-items: baseline; gap: var(--board-space-2); padding: var(--board-space-3) 0; border-bottom: 1px solid var(--board-line); font-size: var(--board-size-ui); }
.gb-notification-list > li:last-child { border-bottom: 0; }
.gb-notification-list > li > a, .gb-notification-list > li > span { flex: 1; min-width: 0; color: var(--board-text); }
.gb-notification-list > li.gb-unread { font-weight: var(--board-weight-bold); }
.gb-notification-list > li.gb-unread::before { content: ""; flex: none; align-self: center; width: 0.4rem; height: 0.4rem; border-radius: 50%; background: var(--board-ink); }
.gb-notification-list time { flex: none; color: var(--board-dim); font-size: var(--board-size-meta); font-weight: var(--board-weight-normal); font-variant-numeric: tabular-nums; }
.gb-notification-delete { flex: none; margin: 0; }
.gb-notifications .gb-notification-list .gb-notification-delete > button[type="submit"] { min-height: 0; padding: 0; border: 0; border-radius: 0; background: none; color: var(--board-dim); font-size: var(--board-size-meta); font-weight: var(--board-weight-normal); }
.gb-notifications .gb-notification-list .gb-notification-delete > button[type="submit"]:hover { background: none; color: var(--board-text); opacity: 1; }
/* A 403/404 sits on the page ground, not a card: the code in the brand at 8rem, what happened, and two ways onward — home as the filled btn-primary, the same address again quiet (board-skin.md §9). Production's measures, off the skin's scale, so literals. */
.gb-error-page { padding-block: clamp(3rem, 14vh, 8rem); text-align: center; }
.gb-error-page .gb-status { margin: 0 0 0.8rem; font-size: 8rem; font-weight: 900; line-height: 1; color: var(--board-accent); }
.gb-error-page h1 { margin: 0 0 0.75rem; font-size: 2.25rem; font-weight: var(--board-weight-title); line-height: 1.25; }
.gb-error-page .gb-text { margin: 0 0 1.5rem; font-size: 1rem; color: var(--board-dim); }
.gb-error-page .gb-go { display: inline-flex; flex-wrap: wrap; justify-content: center; gap: 0.5rem; }
@media (max-width: 47.99rem) {
  .gb-error-page .gb-status { font-size: 4rem; }
  .gb-error-page h1 { font-size: 1.75rem; }
}

table:not(.board-table) { border-collapse: collapse; }
table:not(.board-table) th, table:not(.board-table) td { padding: var(--board-space-2) var(--board-space-3); border-bottom: 1px solid var(--board-line); text-align: left; }
dl { margin: 0; }
dt { font-size: var(--board-size-meta); color: var(--board-dim); }
dd { margin: 0 0 var(--board-space-2); }
