/*
 * This is a manifest file that'll be compiled into application.css.
 *
 * With Propshaft, assets are served efficiently without preprocessing steps. You can still include
 * application-wide styles in this file, but keep in mind that CSS precedence will follow the standard
 * cascading order, meaning styles declared later in the document or manifest will override earlier ones,
 * depending on specificity.
 *
 * Consider organizing styles into separate files for maintainability.
 */

/* Phase 21.4 — mobile overflow guardrails. Keep form controls and
   embedded media inside their container at narrow viewports, and wrap
   unbreakable tokens (long URLs, single-token usernames in user
   content) so they don't force a horizontal scroll. The defaults that
   force overflow on phone-class viewports:
   - `<input maxlength="72">` (password fields) renders ~576px wide in
     Chrome when no `size` attribute is set;
   - `<textarea cols="72">` (post body editor) renders ~576px wide;
   - markdown-rendered URLs in post/comment bodies are unbreakable
     tokens that ignore default whitespace wrapping.
   `box-sizing: border-box` keeps the `max-width: 100%` clamp honest by
   counting padding/border inside the constraint. Pinned by
   test/system/mobile_layout_test.rb at 390x844. */
input,
textarea,
select,
button {
  max-width: 100%;
  box-sizing: border-box;
}

/* UX plan §9.1 — tap-target minimum (WCAG 2.5.8). Buttons, submit/
   reset/button inputs, and selects render at ~21px tall by default in
   Chrome, just under the 24×24 CSS px minimum. The min-height clamp
   lifts default chrome to the threshold without altering any view
   that has already opted into a larger size. min-width covers the
   square-button case (icon-only or one-glyph submits). Pinned by
   test/system/accessibility/tap_targets_test.rb. */
button,
input[type="submit"],
input[type="button"],
input[type="reset"],
select {
  min-height: 24px;
}

button,
input[type="submit"],
input[type="button"],
input[type="reset"] {
  min-width: 24px;
}

/* UX plan §9.1 — checkbox/radio tap targets (WCAG 2.5.8 design call).
   Native checkbox/radio inputs render ~13×13 in Chrome — well under the
   24×24 minimum. Sizing the inputs themselves to 24×24 distorts the
   glyph; instead, the effective tap target is the label that contains
   (or `for=`-references) the input, since clicking the label toggles
   the input. Authoring rule: every checkbox/radio is wrapped in a
   <label class="form-check"> so the label IS the rendered tap region.
   This rule clamps that label to a 24px hit region without inflating
   the native control. inline-flex keeps the input and short label text
   on one line; `flex-wrap: wrap` lets long children (e.g. the
   `<strong>name</strong> <span>description</span>` pair on the
   /communities/new radios) reflow onto a second row at narrow viewports
   instead of overflowing the parent. `min-height: 24px` is the WCAG
   floor; the small `gap` prevents an adjacent-target collision.
   Pinned by test/system/accessibility/tap_targets_test.rb. */
label.form-check {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.375rem;
  min-height: 24px;
  line-height: 1.5;
}

/* UX plan §9.1 — site-nav links render as default-styled inline <a>
   (~17px tall, no padding) inside the global header. WCAG 2.5.8 makes
   no inline exception for chrome-style nav links, so promote them to
   inline-block with vertical padding. The `.site-nav a` selector
   covers every <li>'d nav entry (Feed, New post, My journal, etc.)
   and the notifications bell anchor at #notifications-bell. */
.site-nav a {
  display: inline-block;
  min-height: 24px;
  padding: 0.125rem 0.25rem;
  line-height: 1.5;
}

img {
  max-width: 100%;
  height: auto;
}

main {
  overflow-wrap: break-word;
}

/* UX plan §9.4 — visible focus indicator (regression guard). WCAG
   2.4.7 requires every keyboard-focusable element to render a clearly
   distinguishable focus state, and 1.4.11 sets a 3:1 non-text
   contrast minimum for the indicator itself. Browsers already render
   a default outline on `:focus`, so the audit's main risk is silent
   regression: a future reset (e.g. `*:focus { outline: none }`,
   `outline-color: transparent`, `outline-style: none`,
   `outline-width: 0`) at equal-or-lower specificity would strip the
   indicator for every interactive element at once. Declaring the
   rule explicitly here pins the default. A *higher-specificity*
   per-component override (e.g. `.button:focus-visible`) can still
   opt out, by design — the rendered per-surface audit is the P1
   follow-up that covers that path.
   Color `#0a3d8f` is reused from `.community-tag__badge--required`
   (~9:1 on white, well above the 3:1 non-text threshold), so no new
   hex needs registering in `test/lib/color_contrast_test.rb`. Pinned
   by `test/lib/focus_indicator_test.rb`. */
:focus-visible {
  outline: 2px solid #0a3d8f;
  outline-offset: 2px;
}

/* UX plan §2.5 / §10.3 — post form spacing. Replaces the repeated
   inline `style="margin-top:8px;"` and `margin-top:12px;` that used
   to live on every field row of `app/views/posts/_form.html.erb`.
   Three classes preserve the exact pre-cleanup visual rhythm:
   - `.post-form__field` is the default top margin for stacked rows
     (body, privacy, custom-list, comment-policy, userpic, content-
     warning, tags).
   - `.post-form__community-target` keeps the selector's spacing as a
     bottom margin so an upstream quote-banner stays flush against it
     (the original layout) while the title row below picks up its own
     top margin via `.post-form__field`.
   - `.post-form__actions` carries the slightly larger submit-row gap.
   Pin: `test/integration/community_tags_test.rb` (post_form_renders_
   without_inline_styles) asserts the partial emits no `style=` on
   field rows. */
.post-form__field {
  margin-top: 0.5rem;
}

.post-form__community-target {
  margin-bottom: 0.5rem;
}

.post-form__actions {
  margin-top: 0.75rem;
}

/* UX plan §10.3 — post form hint rows. Replaces the `<br><small>` pair
   that previously used markup-as-spacing under each input. Hint
   variants (`__required-tags`, `__manage-community-tags`) inherit
   the same block layout while keeping their dedicated class hooks
   for the targeted assert_select calls in
   `test/integration/community_tags_test.rb`. */
.post-form__hint {
  display: block;
  margin-top: 0.25rem;
}

/* UX plan §5.3 — feed header + new-posts stripe. */
.feed__header {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
}

.feed__title {
  margin: 0;
}

.feed__mark-all-read-form {
  margin: 0;
}

.feed__new-posts-indicator {
  margin: 0.5rem 0;
  padding: 0.375rem 0.75rem;
  background: #fff3cd;
  color: #664d03;
  border-radius: 2px;
}

/* UX plan §4.5 — moderator-facing post queue. Badges/chips ride
   alongside form controls per row, so contrast and tap-target
   geometry have to read at 390px without crowding the approve/reject
   forms below them.
   - The "Pending approval" chip is rendered through the shared
     `.status-badge` module (§10.2) with the per-surface
     `.community-post-queue__status-badge` hook kept on the element so
     existing layout assertions stay pinned to a queue-specific node.
   - `.community-tag__badge--required` / `--optional` are the chip
     surfaces. Required keeps an outline so a colorblind reader can
     still distinguish vocabulary chips from free-form tags. */
.community-post-queue__row {
  padding: 0.5rem 0;
  border-bottom: 1px solid #eee;
}

.community-post-queue__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
}

.community-post-queue__excerpt {
  color: #444;
  margin: 0.25rem 0;
}

.community-tag__badge {
  display: inline-block;
  padding: 0.125rem 0.5rem;
  border-radius: 0.25rem;
  font-size: 0.875rem;
  margin-right: 0.25rem;
}

.community-tag__badge--required {
  background: #e7f1ff;
  color: #0a3d8f;
  border: 1px solid #0a3d8f;
}

.community-tag__badge--optional {
  background: #f1f1f1;
  color: #444;
}

/* UX plan §4.6 — membership-request queue rows mirror the §4.5 post-
   queue chrome so mods learn one decision pattern. The "Pending
   approval" chip renders through the shared `.status-badge--pending`
   module (§10.2); the `.membership-request__status-badge` hook stays
   on the element so per-surface viewport assertions hold. */
.membership-request__row {
  padding: 0.5rem 0;
  border-bottom: 1px solid #eee;
}

.membership-request__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
}

/* UX plan §10.2 — single status-badge chip surface. Six modifiers cover
   the lifecycle/operator states the app names today (draft, published,
   pending approval, rejected, suspended, deleted). One shared geometry
   keeps the chip the same height across surfaces; per-state color
   conveys urgency at a glance (amber = needs review, red = sanctioned,
   green = live, gray = inert). Each pair is registered in the
   `ColorContrastTest::PAIRS` table so a future tweak that drops below
   AA fails at lint. Reused by post show/edit, community queue,
   membership-request queue, and admin user/community show. */
.status-badge {
  display: inline-block;
  padding: 0.125rem 0.5rem;
  border-radius: 0.25rem;
  font-size: 0.875rem;
  font-weight: 600;
  line-height: 1.4;
}

.status-badge--pending {
  background: #fff3cd;
  color: #664d03;
}

.status-badge--rejected {
  background: #fde2e2;
  color: #8a1f1f;
}

.status-badge--suspended {
  background: #fde2e2;
  color: #8a1f1f;
}

.status-badge--deleted {
  background: #eaeaea;
  color: #3a3a3a;
}

.status-badge--draft {
  background: #eaeaea;
  color: #3a3a3a;
}

.status-badge--published {
  background: #e6f4ea;
  color: #195a2a;
}

/* UX plan §7.2 — admin index tables (users, communities) carry 6–7
   columns of operator data and are not reflowed into stacked cards.
   The `.admin-table-scroll` wrapper contains the horizontal overflow
   inside the region so the document does not scroll past 390px, and
   `role="region"` + `aria-label` announces the affordance to AT.
   `tabindex="0"` makes the region focusable so keyboard users can pan
   it with arrow keys. The 2px outline on focus reuses the global
   `:focus-visible` color (#0a3d8f) for the keyboard cue.
   `position: relative` anchors the left/right edge-fade pseudo-
   elements to the visible clip box, not the scroll content, so the
   fades stay stuck to the viewport edges as the operator pans.
   Pinned by test/system/admin/index_layout_test.rb. */
.admin-table-scroll {
  position: relative;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  max-width: 100%;
}

.admin-table-scroll > table {
  /* Browser default `table` width is content-shrink; force the table to
     reach its natural intrinsic width so the wrapper's scrollbar is the
     thing that overflows, not the table column-collapse heuristic. */
  width: max-content;
  min-width: 100%;
}

/* Sighted "you can scroll this" cue inside the region. aria-hidden on
   the <p> so AT doesn't double-announce alongside the wrapper's
   aria-label; this is purely visual.

   Visibility is layered:
   - No-JS fallback: a width-based media query (<=800px) where the
     7-col admin tables empirically overflow. Heuristic, but the only
     thing the engine knows when JS is off.
   - With JS (admin_table_scroll_controller): the controller adds
     `--js` once Stimulus connects and `--overflowing` once it
     measures actual overflow. The JS-active rules then override the
     media-query heuristic with the measurement. Specificity 0,2,0
     beats the 0,1,0 base rule regardless of cascade order, so the
     two layers compose cleanly without `!important`. */
.admin-table-scroll__hint {
  display: none;
  margin: 0 0 0.25rem;
  font-size: 0.8125rem;
  font-style: italic;
  color: #555;
}

@media (max-width: 800px) {
  .admin-table-scroll__hint {
    display: block;
  }
}

.admin-table-scroll.admin-table-scroll--js .admin-table-scroll__hint {
  display: none;
}

.admin-table-scroll.admin-table-scroll--overflowing .admin-table-scroll__hint {
  display: block;
}

/* Edge-fade cue for off-screen content. Pseudo-elements pin to the
   wrapper's clip box (because `.admin-table-scroll` is `position:
   relative`), so the fades stay at the visible edges as the operator
   pans. Opacity 0 by default; the JS controller adds
   `--overflowing` when there is content off-screen, and `--at-end` /
   `--at-start` when the operator has panned to that edge. The fades
   then dim against whichever edge has nothing left.
   `rgba(255,255,255,...)` matches the page background. Not a hex, so
   ColorContrastTest::PAIRS doesn't need a new entry. */
.admin-table-scroll::before,
.admin-table-scroll::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  width: 1.5rem;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.12s ease;
}

.admin-table-scroll::before {
  left: 0;
  background: linear-gradient(to right, rgba(255, 255, 255, 0.95), rgba(255, 255, 255, 0));
}

.admin-table-scroll::after {
  right: 0;
  background: linear-gradient(to left, rgba(255, 255, 255, 0.95), rgba(255, 255, 255, 0));
}

.admin-table-scroll.admin-table-scroll--overflowing:not(.admin-table-scroll--at-start)::before,
.admin-table-scroll.admin-table-scroll--overflowing:not(.admin-table-scroll--at-end)::after {
  opacity: 1;
}

/* Content-warning blur — see app/javascript/controllers/post_blur_controller.js */
.post-body--blurred {
  filter: blur(12px);
  pointer-events: none;
  user-select: none;
  transition: filter 0.2s ease;
}

/* Comment tree indentation — depth is set via inline style (margin-left)
   generated by the _comment partial from the comment's `depth` field. */
.comment {
  padding: 0.75rem 0;
  border-bottom: 1px solid #eee;
}

.comment__header {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.25rem;
}

.comment__meta {
  color: #666;
  font-size: 0.875rem;
}

.comment__body--tombstone {
  color: #6c6c6c;
  font-style: italic;
}

/* Phase 21.3 — accessibility: skip-to-main-content link. Visually hidden
   off-canvas until it receives keyboard focus, then anchors to top of
   viewport so screen reader / keyboard users can jump past the site nav.
   `<main id="main" tabindex="-1">` is the target; the negative tabindex
   lets the link's anchor jump move focus into <main> without making it a
   tab stop in normal navigation. */
.skip-link {
  position: absolute;
  left: -10000px;
  top: auto;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.skip-link:focus {
  position: static;
  width: auto;
  height: auto;
  padding: 0.5rem 1rem;
  background: #000;
  color: #fff;
  text-decoration: underline;
}

/* UX plan §9.7 — visually-hidden helper for the polite-live announcer
   region rendered in app/views/layouts/application.html.erb. Off-canvas
   (not display:none) so the node stays in the accessibility tree and AT
   engines pick up live-region insertions. Mirrors the canonical "sr-only"
   recipe (1×1 clipped box, no overflow, no pointer events). */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Phase 21.3 — flash messages render as pre-existing live regions
   (role="alert" / role="status") inside <main> via
   app/views/shared/_flash.html.erb. Both containers mount on every page
   even when no flash is set so AT can announce content inserted later.
   `:not(:empty)` gates visual margin so empty containers take zero
   layout — `display:none` would remove them from the AT tree and break
   the live-region contract. Inline color styles previously on per-view
   tag.div calls are retained here so the visual cue (red for alerts,
   green for notices) survives the centralization. */
.flash__message {
  margin: 0;
}

.flash__message:not(:empty) {
  margin-bottom: 0.75rem;
}

.flash__message--alert {
  color: #b00020;
}

.flash__message--notice {
  color: #1f7a1f;
}

/* Phase 21.3 — accessible foreground colors for view chrome that
   previously used inline `style="color:red"` / `color:green`. CSS keyword
   `red` (#ff0000) is 4.0:1 on white and fails WCAG AA; `#b00020` is
   ~7.3:1 (matches the alert flash). `green` (#008000) is 5.1:1 — already
   AA-pass — but centralizing it removes another inline-style site and
   keeps the badge consistent with the notice flash (#1f7a1f, ~5.4:1).
   Pinned by test/lib/color_contrast_test.rb. */
.field-error {
  color: #b00020;
}

.button--danger {
  color: #b00020;
}

.userpic__default-badge {
  color: #1f7a1f;
  font-weight: bold;
}

/* UX plan §10.3 — initials-disc fallback when no userpic is attached.
   `UserpicsHelper#placeholder_tag` inline-styles the dynamic bits
   (width/height, font-size, background-color in hsl). Static chrome —
   border-radius, color, font, centering — lives here so the inline
   payload stays minimal. White (#fff) on hsl(h, 55%, 25%) clears WCAG
   1.4.3 AA 4.5:1 across every hue (worst-case h≈60 ≈ 6.1:1). */
.userpic-placeholder {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  overflow: hidden;
  color: #fff;
  font-family: system-ui, sans-serif;
  font-weight: 600;
  text-align: center;
  vertical-align: middle;
  user-select: none;
}

/* UX plan §1.1 — guest landing hero on `/` (sessions#new). Layout
   stays a single column on phone-class viewports so the hero, sign-up
   CTA, and the in-page sign-in anchor all sit above the fold at
   390px. Pillar list is plain text — no decorative bullets that would
   compete with the four short pillar lines. The form below remains
   the canonical sign-in surface; the `.landing-hero__cta` to
   `#sign-in-form` is just a same-page anchor so keyboard users land
   on the heading, not on an autofocused input. */
.landing-hero {
  margin: 0 0 1.5rem;
}

.landing-hero__title {
  margin: 0 0 0.5rem;
}

.landing-hero__tagline {
  margin: 0 0 0.75rem;
  color: #555;
}

.landing-hero__pillars {
  list-style: none;
  padding: 0;
  margin: 0 0 1rem;
  display: grid;
  gap: 0.25rem;
}

.landing-hero__ctas {
  margin: 0 0 1rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.landing-hero__cta {
  display: inline-block;
  padding: 0.5rem 0.875rem;
  border: 1px solid #261b23;
  border-radius: 4px;
  text-decoration: none;
  color: #261b23;
  min-height: 24px;
}

.landing-hero__cta--primary {
  background: #261b23;
  color: #fff;
}

/* Phase 19.5 — dynamic error pages (see app/views/errors/*,
   app/views/layouts/error.html.erb). Standalone, no dependency on
   the main application layout. */
.error-layout {
  margin: 0;
  font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  background: #fff;
  color: #261b23;
  min-height: 100dvh;
  display: grid;
  place-items: center;
}

.error-page {
  padding: 2rem 1rem;
  width: 100%;
}

.error-page__card {
  max-width: 36rem;
  margin: 0 auto;
  text-align: center;
  display: grid;
  gap: 1rem;
}

.error-page__status {
  font-size: 3rem;
  font-weight: 700;
  color: #d30001;
  margin: 0;
  letter-spacing: -0.02em;
}

.error-page__title {
  font-size: 1.5rem;
  margin: 0;
  line-height: 1.3;
}

.error-page__body {
  color: #555;
  margin: 0;
}

.error-page__home {
  color: inherit;
  font-weight: 700;
  text-decoration: underline;
  text-underline-offset: 0.1em;
}

/* UX plan §11.4 — operator-greppable handle. Dimmed under the home
   link so it reads as metadata for support, not part of the user's
   recovery path. Monospace on the id itself so a user reading it
   out loud or copy-pasting doesn't lose hyphens / 0-vs-O. */
.error-page__reference {
  color: #666;
  font-size: 0.875rem;
  margin: 0;
}

.error-page__reference-id {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  word-break: break-all;
}

/* UX plan §9 — email-verified confirmation moment. Same green accent
   as `.flash__message--notice` (#1f7a1f on #fff, ~5.4:1) so a verifier
   reads the banner as success at-a-glance without introducing a new
   hue. Border-left bar gives the at-a-glance "moment" weight that a
   one-line flash doesn't carry. CTA list is a flex row that wraps at
   narrow viewports — each link inherits the global tap-target lift
   (§9.1) via the `button, a` chrome rules earlier in this file. */
.email-verified-banner {
  margin: 1rem 0;
  padding: 0.75rem 1rem;
  border-left: 4px solid #1f7a1f;
  background: #fff;
}

.email-verified-banner__heading {
  color: #1f7a1f;
  margin: 0 0 0.5rem;
}

.email-verified-banner__cta {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  padding: 0;
  margin: 0;
}

.email-verified-banner__cta-link {
  display: inline-block;
}

/* UX plan §10.1 — shared action-button chrome.
   `ActionButtonsHelper#action_button_class` is the single render path;
   `variant:` is closed-set (`VARIANTS`) so a typo fails at render
   time. Three solid variants carry the launch palette: `primary`
   reuses the landing-CTA dark accent (#fff on #261b23, already in
   ColorContrastTest::PAIRS), `secondary` is its ghost twin
   (#261b23 on #fff with the same border), `danger` reuses the
   `.button--danger` red hex (#b00020 on #fff) so the destructive
   color vocabulary stays consistent across the app. `--inline`
   tightens padding/font-size for row-list action chrome (boundaries,
   comment actions) without changing color. `[disabled]` carries the
   disabled state via the native attribute — no `--disabled` modifier
   so tests / JS can branch on the attribute without parsing class
   lists.
   `display: inline-block` lets `link_to` adopt `.btn` (post edit,
   admin edit) and pick up the same chrome. The `min-height: 24px`
   on `.btn` itself — not relying on the §9.1 `button {}` rule which
   does not select anchors — is what pins the WCAG 2.5.8 tap-target
   floor for the anchor `.btn` cases; the `.btn--inline` padding +
   font-size are tuned to keep computed height above 24px even with
   tighter chrome. */
.btn {
  display: inline-block;
  min-height: 24px;
  padding: 0.5rem 0.875rem;
  border-radius: 4px;
  border: 1px solid transparent;
  font-weight: 500;
  line-height: 1.4;
  text-decoration: none;
  cursor: pointer;
}

.btn--primary {
  background: #261b23;
  color: #fff;
  border-color: #261b23;
}

.btn--secondary {
  background: #fff;
  color: #261b23;
  border-color: #261b23;
}

.btn--danger {
  background: #fff;
  color: #b00020;
  border-color: #b00020;
}

.btn--inline {
  padding: 0.25rem 0.5rem;
  font-size: 0.875rem;
}

.btn[disabled] {
  opacity: 0.6;
  cursor: not-allowed;
}

/* UX plan §10.5 — submit-row spacing pulled off two simple forms. */
.custom-list-form__actions,
.community-form__actions {
  margin-top: 0.5rem;
}

/* UX plan §10.5 — userpics gallery chrome (grid + cards + actions). */
.userpics-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 1rem;
}

.userpics-grid__card {
  border: 1px solid #ddd;
  padding: 0.5rem;
  text-align: center;
  position: relative;
}

.userpics-grid__image {
  width: 100px;
  height: 100px;
  object-fit: cover;
  border-radius: 4px;
}

.userpics-grid__keyword {
  margin-top: 0.25rem;
}

.userpics-grid__actions {
  margin-top: 0.5rem;
  display: flex;
  gap: 0.25rem;
  justify-content: center;
  flex-wrap: wrap;
}

.userpics-index__upload {
  margin-top: 1rem;
}

/* UX plan §10.5 — settings/sessions row chrome. */
.session-card {
  margin-bottom: 1rem;
  padding: 0.5rem;
  border: 1px solid #ccc;
}
