/* ===========================================================================
   Only-LANs — brand theme
   ---------------------------------------------------------------------------
   Deliberately a THIN theme. The first rule imports lemmy-ui's own compiled
   `darkly` stylesheet from the same origin; everything below is Only-LANs
   brand delta.

   Why this shape: lemmy-ui's built-in themes are ~290KB compiled Bootstrap
   builds. Copying one into extra_themes would vendor a full Bootstrap
   snapshot that silently rots at every lemmy-ui upgrade. Importing it instead
   means upstream fixes and markup changes flow through automatically and the
   maintained delta stays a few KB — which is what "theme without a persistent
   fork" (wiki link/3596) actually requires.

   Constraint respected: lemmy-ui sends `style-src 'self' 'unsafe-inline'`, so
   a same-origin @import is allowed. An external font/CDN import would NOT be,
   which is why the type stack below is system fonts only.

   NOTE: Cloudflare caches this file. After editing, purge the CF cache for
   /css/themes/only-lans.css or the change will not reach browsers.
   =========================================================================== */

@import url("/css/themes/darkly.css");

/* --- Brand tokens -------------------------------------------------------- */
:root,
[data-bs-theme="dark"] {
  /* Server-room dark: near-black with a cool cast, not flat grey. */
  --ol-bg: #0b0e13;
  --ol-surface: #141922;
  --ol-surface-2: #1b2230;
  --ol-border: #232b38;

  --ol-text: #e6edf3;
  --ol-text-muted: #8b98a9;

  /* Network/status accents — the "link up" LED palette. */
  --ol-accent: #38e07b;
  --ol-accent-2: #22d3ee;
  --ol-warn: #ffb020;
  --ol-danger: #ff5c5c;

  --ol-mono: ui-monospace, "JetBrains Mono", "SFMono-Regular", Menlo, Consolas,
    "Liberation Mono", monospace;
  --ol-sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue",
    Arial, sans-serif;

  /* Map onto the Bootstrap 5.3 custom properties lemmy-ui actually consumes. */
  --bs-body-bg: var(--ol-bg);
  --bs-body-bg-rgb: 11, 14, 19;
  --bs-body-color: var(--ol-text);
  --bs-body-color-rgb: 230, 237, 243;
  --bs-body-font-family: var(--ol-sans);

  --bs-emphasis-color: #ffffff;
  --bs-secondary-color: var(--ol-text-muted);
  --bs-secondary-bg: var(--ol-surface);
  --bs-tertiary-bg: var(--ol-surface-2);

  --bs-border-color: var(--ol-border);
  --bs-border-color-translucent: rgba(255, 255, 255, 0.08);

  --bs-primary: var(--ol-accent);
  --bs-primary-rgb: 56, 224, 123;
  --bs-link-color: var(--ol-accent);
  --bs-link-color-rgb: 56, 224, 123;
  --bs-link-hover-color: #6ef0a2;

  --bs-info: var(--ol-accent-2);
  --bs-warning: var(--ol-warn);
  --bs-danger: var(--ol-danger);

  --bs-code-color: var(--ol-accent-2);
  --bs-font-monospace: var(--ol-mono);
}

body {
  background-color: var(--ol-bg);
  color: var(--ol-text);
  font-family: var(--ol-sans);
}

/* --- Chrome recedes ------------------------------------------------------ */
/* The handoff is explicit: photography dominates, chrome recedes. The navbar
   and cards lose their heavy fills and keep a single hairline instead. */
.navbar {
  background-color: rgba(11, 14, 19, 0.92) !important;
  border-bottom: 1px solid var(--ol-border);
  backdrop-filter: saturate(140%) blur(8px);
}

.card,
.accordion-item,
.list-group-item {
  background-color: var(--ol-surface);
  border: 1px solid var(--ol-border);
}

.post-listing,
.comment-node {
  background-color: transparent;
}

/* --- Image-forward feed (P1) --------------------------------------------- */
/* Stock lemmy-ui renders a small square thumbnail beside a text row, which is
   a discussion-first layout. Only-LANs is a showcase: the photo is the post.
   These rules enlarge the preview and let tall rack shots and wide topology
   diagrams both render without cropping to a square. */
.post-listing .thumbnail,
.post-listing a.thumbnail {
  max-width: 100% !important;
  width: 100% !important;
  height: auto !important;
  aspect-ratio: auto !important;
  overflow: hidden;
  border-radius: 10px;
  border: 1px solid var(--ol-border);
  background-color: var(--ol-surface-2);
}

.post-listing .thumbnail img,
.post-listing img.thumbnail,
.post-listing picture img {
  width: 100%;
  height: auto;
  max-height: 720px;
  object-fit: contain;
  display: block;
  transition: transform 180ms ease;
}

.post-listing .thumbnail:hover img {
  transform: scale(1.012);
}

/* The img-blur used for NSFW/spoiler content must stay blurred — do not let
   the rules above defeat it. */
.post-listing .img-blur,
.post-listing img.img-blur {
  filter: blur(10px);
}

.post-title a {
  font-weight: 600;
  letter-spacing: -0.01em;
}

/* Technical metadata reads as machine output, not prose. */
.post-metadata,
.post-listing .small,
.comment-node .small,
time {
  font-family: var(--ol-mono);
  font-size: 0.82rem;
  color: var(--ol-text-muted);
}

/* --- Status accents ------------------------------------------------------ */
.badge.text-bg-primary,
.btn-primary {
  background-color: var(--ol-accent) !important;
  border-color: var(--ol-accent) !important;
  color: #06210f !important;
  font-weight: 600;
}

.btn-primary:hover {
  background-color: #6ef0a2 !important;
  border-color: #6ef0a2 !important;
}

.btn-outline-primary {
  color: var(--ol-accent);
  border-color: var(--ol-accent);
}

.btn-outline-primary:hover {
  background-color: var(--ol-accent);
  color: #06210f;
}

/* Upvote reads as "link up", downvote as fault. */
.vote-bar .upvote.active,
.upvote.active {
  color: var(--ol-accent) !important;
}

.vote-bar .downvote.active,
.downvote.active {
  color: var(--ol-danger) !important;
}

/* Community names get the cyan activity accent so c/ links are scannable. */
.community-link,
a[href^="/c/"] {
  color: var(--ol-accent-2);
}

/* --- Forms and focus ----------------------------------------------------- */
.form-control,
.form-select,
.form-control:focus,
.form-select:focus {
  background-color: var(--ol-surface-2);
  border-color: var(--ol-border);
  color: var(--ol-text);
}

.form-control:focus,
.form-select:focus,
.btn:focus-visible,
a:focus-visible {
  outline: 2px solid var(--ol-accent-2);
  outline-offset: 2px;
  box-shadow: none;
}

::selection {
  background: rgba(56, 224, 123, 0.28);
}

/* --- Accessibility ------------------------------------------------------- */
/* The hover scale above is decorative; honour a reduced-motion preference. */
@media (prefers-reduced-motion: reduce) {
  .post-listing .thumbnail img,
  .post-listing picture img {
    transition: none;
  }
  .post-listing .thumbnail:hover img {
    transform: none;
  }
}


/* --- IDConnect is the only way in ------------------------------------------
   Every Only-LANs account is created through IDConnect — local password
   registration is refused at the proxy (see nginx/nginx_internal.conf). Only
   the setup admin and a few test accounts have passwords at all, so any
   password field shown to a visitor advertises a path none of them can take.

   Both entry pages get the same treatment: the local form is hidden and the
   provider block is retitled, leaving one heading and one button.

     /login   -> `.login.container-lg`        (component Rp)
     /signup  -> `.home-signup.container-lg`  (component Hp)

   Both render the identical three-row shape, confirmed against the served
   markup:
     row 1 `.row`            -> h1 ("Login" / "Sign Up") + the local form
     row 2 `.row.mt-3.mb-2`  -> empty spacer
     row 3 `.row`            -> h2 "Use external provider" + IDConnect button

   *** SAFE FOR IDCONNECT SIGNUP — verified against the client bundle, not
   assumed. *** A new SSO user must still supply a Lemmy username and an
   application answer. That form belongs to the OAuth callback component (zg,
   routed at /oauth/callback), which renders its OWN bare `div.container-lg`
   with a plain `<form>` child — it does not reuse `home-signup`, does not
   reuse `login`, and its form is not a `.row`. None of the selectors below can
   reach it. This mattered: hiding the wrong container would have broken signup
   for every new member, silently, after the button appeared to work.

   *** PRESENTATION, NOT ENFORCEMENT — DELIBERATELY. ***
   `POST /api/v4/account/auth/login` still accepts a password. That is the
   BREAK-GLASS path: if IDConnect were ever unreachable, blocking it would leave
   nobody able to reach the admin account and no way back in. Registration is a
   different matter and IS enforced, at nginx, because nothing depends on it.

   Break-glass, either works:
     * open devtools and delete the `display: none` rule — the form is still in
       the DOM, fully wired, and submits normally; or
     * comment out that rule and redeploy the theme. With max-age=300 that
       reaches a browser in minutes.

   Failure mode is safe by construction: every rule is additive, so if a
   lemmy-ui upgrade breaks these selectors the pages degrade to stock and the
   forms REAPPEAR. They can never fail into an unreachable site.

   Selectors are keyed to CONTENT, not position. An earlier version used
   `.row:first-of-type`/`:last-of-type` and was wrong twice: `-of-type` counts
   by TAG and the first div child is the hidden TOTP modal, so it matched
   nothing; and there are THREE rows, not two. `:has()` is required; without it
   the rules drop and stock lemmy-ui shows. */

/* The nav offered "Sign Up" beside "Login". Both now lead to the same single
   IDConnect button, so the second link is redundant; "Login" is the more
   universal affordance and the retitled heading covers joining. Keyed to href
   rather than the title attribute, which is translated. One anchor in the
   document, shared by the desktop nav and the mobile collapse menu. */
.navbar a.nav-link[href="/signup"] {
  display: none;
}

/* Hide the local form on both pages. This also takes the page heading, "Forgot
   password", "Stay logged in" and the application-answer editor with it — all
   of which only ever applied to local password accounts. The TOTP modal is a
   sibling, not a child of the hidden row, so 2FA is untouched. */
.login.container-lg > .row:has(h1),
.home-signup.container-lg > .row:has(h1) {
  display: none;
}

/* Retitle the provider section without forking the UI. Upstream's heading names
   the MECHANISM ("Use external provider"); with nothing else on the page, what
   a visitor needs to know is that this is simply how you get in. Done with
   font-size rather than absolute positioning so the replacement stays in normal
   flow and wraps on a narrow phone instead of overflowing. Known wart: the
   original text stays in the accessibility tree, so a screen reader announces
   both strings — redundant, not misleading. Fixing it properly needs
   aria-hidden, which CSS cannot set. */
.login.container-lg > .row:has(h2) h2,
.home-signup.container-lg > .row:has(h2) h2 {
  font-size: 0;
}
.login.container-lg > .row:has(h2) h2::after,
.home-signup.container-lg > .row:has(h2) h2::after {
  content: "Sign in or create your account";
  display: block;
  font-size: 1.4rem;
  font-weight: 500;
}

/* The provider button is now the only action on either page — give it weight. */
.login.container-lg > .row:has(h2) .btn-primary,
.home-signup.container-lg > .row:has(h2) .btn-primary {
  font-size: 1.05rem;
  padding: 0.6rem 1.4rem;
}


/* --- Front page: a hero, without a second surface --------------------------
   The logged-out first impression was poor, and worst exactly where it matters
   most. On a phone, above the fold, a visitor got six filter dropdowns (Sort,
   Type, View, Type, Time, RSS) and "No posts." Every word of the value
   proposition — the tagline, the posting guidance, the house rules — sits in
   the site sidebar, which Bootstrap collapses behind a "Sidebar" button on
   small screens. Nobody presses that button on their first visit.

   Lemmy does render the uploaded site banner, but only inside that same
   sidebar card: about 230px wide on desktop, where a 4:1 banner becomes an
   unreadable strip, and completely hidden on mobile.

   The fix deliberately is NOT a separate landing page. The product thesis is
   that the photos ARE the front page; a marketing page would paper over the
   one question the PoC exists to answer. This is a hero band on the existing
   feed instead — brand and tagline above the fold at every breakpoint, no new
   surface to maintain.

   Scoped to `.home` so it appears on the front page only, never above a
   community or post. The artwork below is GENERATED — edit
   assets/brand/banner.svg and run scripts/build-theme-assets.py. */
.home.container-lg::before {
  content: "";
  display: block;
  width: 100%;
  height: clamp(88px, 12.5vw, 156px);
  margin: 0 0 1.35rem;
  border: 1px solid var(--ol-border);
  border-radius: 12px;
  background-repeat: no-repeat;
  /* left/centre so the wordmark survives the crop at wide aspect ratios */
  background-position: left center;
  background-size: cover;
  /* BEGIN GENERATED banner -- rebuild with scripts/build-theme-assets.py */
  background-image: url("data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 1600 400%22 width=%221600%22 height=%22400%22 role=%22img%22 aria-label=%22Only-LANs - Show us your LAN.%22><defs><linearGradient id=%22f%22 x1=%220%22 y1=%220%22 x2=%221%22 y2=%220%22><stop offset=%220%22 stop-color=%22%230b0e13%22 stop-opacity=%221%22/><stop offset=%22.62%22 stop-color=%22%230b0e13%22 stop-opacity=%22.86%22/><stop offset=%221%22 stop-color=%22%230b0e13%22 stop-opacity=%22.55%22/></linearGradient></defs><rect width=%221600%22 height=%22400%22 fill=%22%230b0e13%22/><rect x=%22620%22 y=%2286%22 width=%2254%22 height=%2238%22 rx=%226%22 fill=%22%23161d27%22/><rect x=%22686%22 y=%2286%22 width=%2254%22 height=%2238%22 rx=%226%22 fill=%22%23161d27%22/><rect x=%22752%22 y=%2286%22 width=%2254%22 height=%2238%22 rx=%226%22 fill=%22%23161d27%22/><rect x=%22818%22 y=%2286%22 width=%2254%22 height=%2238%22 rx=%226%22 fill=%22%23161d27%22/><rect x=%22884%22 y=%2286%22 width=%2254%22 height=%2238%22 rx=%226%22 fill=%22%23161d27%22/><rect x=%22950%22 y=%2286%22 width=%2254%22 height=%2238%22 rx=%226%22 fill=%22%23161d27%22/><rect x=%221016%22 y=%2286%22 width=%2254%22 height=%2238%22 rx=%226%22 fill=%22%23161d27%22/><rect x=%221082%22 y=%2286%22 width=%2254%22 height=%2238%22 rx=%226%22 fill=%22%23161d27%22/><rect x=%221148%22 y=%2286%22 width=%2254%22 height=%2238%22 rx=%226%22 fill=%22%23161d27%22/><rect x=%221214%22 y=%2286%22 width=%2254%22 height=%2238%22 rx=%226%22 fill=%22%23161d27%22/><rect x=%221280%22 y=%2286%22 width=%2254%22 height=%2238%22 rx=%226%22 fill=%22%23161d27%22/><rect x=%221346%22 y=%2286%22 width=%2254%22 height=%2238%22 rx=%226%22 fill=%22%23161d27%22/><rect x=%221412%22 y=%2286%22 width=%2254%22 height=%2238%22 rx=%226%22 fill=%22%23161d27%22/><rect x=%221478%22 y=%2286%22 width=%2254%22 height=%2238%22 rx=%226%22 fill=%22%23161d27%22/><rect x=%221544%22 y=%2286%22 width=%2254%22 height=%2238%22 rx=%226%22 fill=%22%23161d27%22/><rect x=%22620%22 y=%22276%22 width=%2254%22 height=%2238%22 rx=%226%22 fill=%22%23161d27%22/><rect x=%22686%22 y=%22276%22 width=%2254%22 height=%2238%22 rx=%226%22 fill=%22%23161d27%22/><rect x=%22752%22 y=%22276%22 width=%2254%22 height=%2238%22 rx=%226%22 fill=%22%23161d27%22/><rect x=%22818%22 y=%22276%22 width=%2254%22 height=%2238%22 rx=%226%22 fill=%22%23161d27%22/><rect x=%22884%22 y=%22276%22 width=%2254%22 height=%2238%22 rx=%226%22 fill=%22%23161d27%22/><rect x=%22950%22 y=%22276%22 width=%2254%22 height=%2238%22 rx=%226%22 fill=%22%23161d27%22/><rect x=%221016%22 y=%22276%22 width=%2254%22 height=%2238%22 rx=%226%22 fill=%22%23161d27%22/><rect x=%221082%22 y=%22276%22 width=%2254%22 height=%2238%22 rx=%226%22 fill=%22%23161d27%22/><rect x=%221148%22 y=%22276%22 width=%2254%22 height=%2238%22 rx=%226%22 fill=%22%23161d27%22/><rect x=%221214%22 y=%22276%22 width=%2254%22 height=%2238%22 rx=%226%22 fill=%22%23161d27%22/><rect x=%221280%22 y=%22276%22 width=%2254%22 height=%2238%22 rx=%226%22 fill=%22%23161d27%22/><rect x=%221346%22 y=%22276%22 width=%2254%22 height=%2238%22 rx=%226%22 fill=%22%23161d27%22/><rect x=%221412%22 y=%22276%22 width=%2254%22 height=%2238%22 rx=%226%22 fill=%22%23161d27%22/><rect x=%221478%22 y=%22276%22 width=%2254%22 height=%2238%22 rx=%226%22 fill=%22%23161d27%22/><rect x=%221544%22 y=%22276%22 width=%2254%22 height=%2238%22 rx=%226%22 fill=%22%23161d27%22/><rect x=%22884%22 y=%2286%22 width=%2254%22 height=%2238%22 rx=%226%22 fill=%22%2338e07b%22 opacity=%220.3%22/><rect x=%221214%22 y=%2286%22 width=%2254%22 height=%2238%22 rx=%226%22 fill=%22%2338e07b%22 opacity=%220.22%22/><rect x=%22752%22 y=%22276%22 width=%2254%22 height=%2238%22 rx=%226%22 fill=%22%2338e07b%22 opacity=%220.26%22/><rect x=%221346%22 y=%22276%22 width=%2254%22 height=%2238%22 rx=%226%22 fill=%22%2338e07b%22 opacity=%220.18%22/><rect width=%221600%22 height=%22400%22 fill=%22url(%23f)%22/><path d=%22M190.0 168.7 L190.0 201.4%22 stroke=%22%232a6f4b%22 stroke-width=%2212.7%22 stroke-linecap=%22round%22 fill=%22none%22/><path d=%22M124.6 201.4 L255.4 201.4%22 stroke=%22%232a6f4b%22 stroke-width=%2212.7%22 stroke-linecap=%22round%22 fill=%22none%22/><path d=%22M124.6 201.4 L124.6 225.3%22 stroke=%22%232a6f4b%22 stroke-width=%2212.7%22 stroke-linecap=%22round%22 fill=%22none%22/><path d=%22M255.4 201.4 L255.4 225.3%22 stroke=%22%232a6f4b%22 stroke-width=%2212.7%22 stroke-linecap=%22round%22 fill=%22none%22/><rect x=%22161.7%22 y=%22126.7%22 width=%2256.6%22 height=%2242.0%22 rx=%228.4%22 fill=%22%2338e07b%22/><rect x=%22181.5%22 y=%22125.7%22 width=%2217.0%22 height=%2210.1%22 rx=%223.4%22 fill=%22%230b0e13%22/><rect x=%2299.2%22 y=%22225.3%22 width=%2250.8%22 height=%2240.0%22 rx=%228.0%22 fill=%22%232a6f4b%22/><rect x=%22117.0%22 y=%22224.3%22 width=%2215.2%22 height=%229.6%22 rx=%223.2%22 fill=%22%230b0e13%22/><rect x=%22230.0%22 y=%22225.3%22 width=%2250.8%22 height=%2240.0%22 rx=%228.0%22 fill=%22%2338e07b%22/><rect x=%22247.8%22 y=%22224.3%22 width=%2215.2%22 height=%229.6%22 rx=%223.2%22 fill=%22%230b0e13%22/><text x=%22330%22 y=%22196%22 font-family=%22system-ui,-apple-system,'Segoe UI',Roboto,Helvetica,Arial,sans-serif%22 font-size=%2288%22 font-weight=%22700%22 letter-spacing=%22-2%22 fill=%22%23e6edf3%22>Only-LANs</text><text x=%22334%22 y=%22256%22 font-family=%22ui-monospace,'JetBrains Mono',SFMono-Regular,Menlo,Consolas,monospace%22 font-size=%2234%22 fill=%22%238b98a9%22>Show us your LAN.</text></svg>");
  /* END GENERATED banner */
}

/* Chrome recedes, per the brand note at the top of this file. The sort/type/
   view controls are useful but they are not what a first-time visitor came for,
   and they were the loudest thing on the page. They come back to full strength
   the moment someone reaches for them, so nothing is harder to use — only
   quieter until wanted. */
.home .main-content-wrapper .row.row-cols-auto {
  opacity: 0.68;
  transition: opacity 140ms ease;
}
.home .main-content-wrapper .row.row-cols-auto:hover,
.home .main-content-wrapper .row.row-cols-auto:focus-within {
  opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  .home .main-content-wrapper .row.row-cols-auto {
    transition: none;
  }
}

/* On a narrow screen the hero is scaled down so far that the tagline stops
   reading. Making the band taller lets `cover` scale the artwork UP instead of
   down: the patch-panel motif on the right crops away, which is decoration,
   and the mark and wordmark on the left — which are not — get bigger. */
@media (max-width: 575.98px) {
  .home.container-lg::before {
    height: 132px;
  }
}

/* Lemmy also renders the uploaded banner inside the site sidebar card, where it
   is a ~230px-wide unreadable strip. The hero above now carries the brand, so
   the duplicate goes. Scoped to `.home` on purpose: the same class carries
   COMMUNITY banners elsewhere, and those are content, not chrome. */
.home .banner-icon-header {
  display: none;
}
