/*
 * 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.
 */



/* Allow light/dark color scheme auto-switching via prefers-color-scheme */
.color-scheme-light-dark {
  color-scheme: light dark;
}

html {
  scroll-behavior: smooth;
}

/* iOS Safe Area Support */
.safe-area-top {
  padding-top: env(safe-area-inset-top);
}

.safe-area-bottom {
  padding-bottom: env(safe-area-inset-bottom);
}

.modal-backdrop {
  /* Full screen coverage for modal backdrop - covers everything including safe areas */
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  width: 100%;
  height: 100%;
  /* No padding - let it cover the entire screen including notch/status bar */
}

/* Floating admin navigation footprint is shared by page clearance and create FABs. */
.admin-mobile-nav {
  position: fixed;
  z-index: 50;
  left: max(16px, env(safe-area-inset-left) + 16px);
  right: max(16px, env(safe-area-inset-right) + 16px);
  bottom: calc(12px + env(safe-area-inset-bottom, 0px));
  max-width: 360px;
  margin-inline: auto;
  min-height: 64px;
  box-sizing: border-box;
  padding: 3px;
  border: 1px solid rgb(228 228 231);
  border-radius: 24px;
  background: white;
  box-shadow: 0 4px 16px rgb(0 0 0 / 0.12);
}
.admin-mobile-nav__actions { display: flex; min-height: 56px; }
.admin-mobile-nav__action {
  flex: 1; min-height: 56px; padding: 6px 4px;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 4px; color: rgb(82 82 91); border-radius: 18px;
  font-size: 12px; line-height: 1.2; text-decoration: none;
}
.admin-mobile-nav__action:focus-visible { outline: 2px solid currentColor; outline-offset: -3px; }
.admin-mobile-nav__action--active { color: black; font-weight: 600; }
.admin-mobile-fab { bottom: calc(var(--admin-mobile-nav-height, 64px) + 12px + env(safe-area-inset-bottom, 0px) + 16px); right: max(1.5rem, env(safe-area-inset-right) + 1rem); }
.admin-main { padding-bottom: calc(var(--admin-mobile-nav-height, 64px) + 12px + env(safe-area-inset-bottom, 0px) + 16px); }
@media (min-width: 768px) { .admin-mobile-nav, .admin-mobile-fab { display: none; } .admin-main { padding-bottom: 0; } }
/* The floating bar must never sit above a modal navigation surface: hide it
   (visibility, not display) while omni-search, the help drawer, or any
   data-controller="modal" overlay is open. Reserved page padding lives on
   .admin-main, which is untouched here.

   The mobile menu is deliberately NOT in this list: it is a non-modal
   `popover="auto"` panel, so the bar stays visible (and clickable) below it
   instead of being hidden. */
body:has([data-omni-search-target="overlay"]:not(.hidden)) .admin-mobile-nav,
body:has([data-help-drawer-target="overlay"]:not(.hidden)) .admin-mobile-nav,
body:has([data-controller="modal"][data-modal-visible="true"]) .admin-mobile-nav {
  visibility: hidden;
}
@media (prefers-color-scheme: dark) {
  .admin-mobile-nav { background: black; border-color: rgb(38 38 38); }
  .admin-mobile-nav__action { color: rgb(163 163 163); }
  .admin-mobile-nav__action--active { color: white; }
}

/* Non-modal mobile menu panel — a native `popover="auto"`
   element. The browser supplies top-layer stacking (so it paints above the
   floating tab bar), light-dismiss on outside tap and Escape-to-close, without
   making the page inert: no scrim, no body scroll lock, no focus trap. The
   panel is the bounded surface itself — anchored above the bar and never
   covering the viewport — so the bar below stays clickable. */
.admin-mobile-menu-panel {
  /* Reset the UA popover box (fixed, inset:0, margin:auto, border, padding). */
  position: fixed;
  inset: auto;
  left: max(12px, env(safe-area-inset-left) + 12px);
  right: max(12px, env(safe-area-inset-right) + 12px);
  bottom: calc(var(--admin-mobile-nav-height, 70px) + 12px + env(safe-area-inset-bottom, 0px) + 8px);
  width: auto;
  height: auto;
  max-width: none;
  /* Available viewport minus the fixed mobile header (48px + notch) and the
     floating nav footprint (measured height + its 12px offset + safe-area). */
  max-height: calc(100vh - (48px + env(safe-area-inset-top, 0px)) - (var(--admin-mobile-nav-height, 70px) + 12px + env(safe-area-inset-bottom, 0px)) - 24px);
  max-height: calc(100dvh - (48px + env(safe-area-inset-top, 0px)) - (var(--admin-mobile-nav-height, 70px) + 12px + env(safe-area-inset-bottom, 0px)) - 24px);
  margin: 0;
  padding: 0;
  border: 1px solid rgb(228 228 231);
  border-radius: 20px;
  background: white;
  box-shadow: 0 16px 40px rgb(0 0 0 / 0.28);
  overflow: hidden;
  flex-direction: column;
  /* The fallback (no Popover API) has no top layer, so it stacks itself below
     the floating bar. Ignored while the panel is in the top layer. */
  z-index: 40;
}
/* `display` lives only in the open states so the UA's closed-popover
   `display: none` is never overridden by this stylesheet. */
.admin-mobile-menu-panel:popover-open { display: flex; }
/* Fallback mode — the panel controller marks the element only where the Popover
   API is missing (or the markup carries no usable `popover` value) and then owns
   the open state with data-menu-open, so this plain box is never left visible
   and tap-blocking. Attribute selectors keep it working without `@supports`. */
.admin-mobile-menu-panel[data-menu-fallback] { display: none; }
.admin-mobile-menu-panel[data-menu-fallback][data-menu-open="true"] { display: flex; }
.admin-mobile-menu-panel__body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  padding: 8px 10px 12px;
}
@media (prefers-color-scheme: dark) {
  .admin-mobile-menu-panel { background: black; border-color: rgb(38 38 38); }
}
/* Motion is opt-in. Nothing about the panel depends on a transition to become
   visible, so the fallback and reduced-motion paths render it fully opaque and
   the two open states above are the only places the animated transform and
   opacity are reset. */
@media (prefers-reduced-motion: no-preference) {
  .admin-mobile-menu-panel {
    transform: translateY(16px);
    opacity: 0;
    transition: transform 250ms ease-out, opacity 250ms ease-out,
      display 250ms allow-discrete, overlay 250ms allow-discrete;
  }
  /* Keep these separate: unsupported :popover-open invalidates a selector list. */
  .admin-mobile-menu-panel:popover-open {
    transform: translateY(0);
    opacity: 1;
  }
  .admin-mobile-menu-panel[data-menu-open="true"] {
    transform: translateY(0);
    opacity: 1;
  }
  @starting-style {
    .admin-mobile-menu-panel:popover-open {
      transform: translateY(16px);
      opacity: 0;
    }
  }
}
/* Panel rows carry the ring inside the panel's rounded, overflow-hidden box. */
.admin-mobile-menu-panel__body :is(a, button, select):focus-visible {
  outline: 2px solid currentColor;
  outline-offset: -2px;
}
/* Without the Popover API the panel would fall back to an invisible but
   tap-blocking box, so hide it here too — this is the no-JavaScript safety net,
   since the fallback controller ([data-menu-fallback] above) cannot run. */
@supports not selector(:popover-open) {
  .admin-mobile-menu-panel { display: none; }
}

/* Watermark Overlay */
.watermark-overlay {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  user-select: none;
  z-index: 10;
}

.watermark-text {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%) rotate(-30deg);
  white-space: nowrap;
  font-size: clamp(1rem, 5vw, 2.5rem);
  font-weight: 700;
  color: rgba(255, 255, 255, 0.35);
  text-shadow: 0 0 4px rgba(0, 0, 0, 0.3);
  letter-spacing: 0.15em;
  text-transform: uppercase;
}

/* For dark images, use a slightly brighter watermark */
.watermark-overlay::before {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.05);
  pointer-events: none;
}

/* ========================================
   Work Gallery
   ======================================== */

.work-gallery-scroll {
  -webkit-overflow-scrolling: touch;
}

/* Prevent text selection during lightbox interaction */
[data-controller="work-lightbox"] {
  user-select: none;
}

/* Gallery slide transition for lightbox */
[data-work-lightbox-target="slide"] {
  will-change: opacity;
}

/* ========================================
   Article Gallery (inline, full-bleed)
   ======================================== */

/* Break an embedded gallery out of the narrow article reading column so it
   spans wider than the text, centered on the column's center line and capped
   so it never dominates the page. Safe because no ancestor in the rendered
   article body clips horizontally, and the article page wrapper carries
   `overflow-x: clip` to absorb the sub-pixel overflow that 100vw produces.
   (overflow-x: clip, not hidden — clip does not establish a scroll container
   and does not affect overflow-y, and it does not reposition fixed elements
   like the lightbox.) */
.article-body .article-bleed {
  width: 100vw;
  max-width: 1200px;
  margin-inline: calc(50% - min(50vw, 600px));
}

/* ========================================
   Lexxy editor — custom attachment layout + persistent delete button
   ========================================
   Lexxy renders custom attachments (our work/gallery embeds) as
   <action-text-attachment> custom elements, which default to display:inline
   and are NOT a positioning context. Two problems follow:
     1. The inline element wrapping a block-level preview card produces
        bizarre layout (the card doesn't behave as its own line).
     2. Each attachment's delete button is position:absolute, so with no
        positioned ancestor they ALL anchor to the editor content container
        and stack at the top of the editor.
   Force the attachments block + relative so each renders on its own line and
   its delete button anchors to it. Visual-only CSS; Lexical's JS model
   (isInline) and exportDOM serialization are unaffected. */
lexxy-editor action-text-attachment {
  display: block;
  position: relative;
  /* A single controlled gap before/after the embed — replaces the stack of
     figure UA margins + my-4 that left a huge gap to the next paragraph. */
  margin-block: 0.75rem;
}

/* Lexxy hides each attachment's delete button (opacity:0) until the
   attachment is selected (.node--selected) — poor discoverability for
   embedded works. Force it visible + clickable, anchored top-right of the
   attachment (which is now position:relative above). The !important wins
   over Lexxy's :where()-wrapped rule. */
lexxy-editor lexxy-node-delete-button {
  opacity: 1 !important;
  pointer-events: auto !important;
  inset-block-start: 6px !important;
  inset-inline-start: auto !important;
  inset-inline-end: 6px !important;
}

/* Editor attachment previews: the <figure> inside our embed cards was
   stacking margins (browser UA figure margin + the preview's my-4 + the empty
   <p> we append after insert), leaving a huge gap before the next paragraph.
   Zero the figure's own margins and let the attachment element's block layout
   provide the only spacing. Selector is scoped to lexxy-editor so it can't
   affect the published .article-body figure styling.

   white-space: normal overrides the `pre-wrap` Lexical sets inline on
   .lexxy-editor__content. pre-wrap makes every newline/indent in our preview
   HTML render as a real line break — the phantom gap. `normal` collapses
   that whitespace to a single space (standard HTML behavior) for embed
   previews only; Lexical's text-editing behavior in the editable root is
   untouched. */
lexxy-editor action-text-attachment figure {
  margin: 0 !important;
  white-space: normal !important;
}

/* ========================================
   Article Body — editorial typography
   ========================================
   Long-form reading column, hand-written (no @tailwindcss/typography
   dependency: this app is importmap-based with no npm, so the plugin can't be
   installed without adding build machinery — Core Belief no. 1). Gives full
   control of the editorial look.

   Constrained measure (~42rem) centered in the page for comfortable reading;
   because the column is centered, full-bleed gallery embeds (.article-bleed)
   still break out to the viewport by contrast.

   Serif via a high-quality system stack — zero assets, no CDN, no FOUT, more
   reliable than self-hosting (matches the intent of the Geist self-hosting
   stance). Self-host a specific serif (Source Serif 4 / Newsreader) later if
   cross-platform pixel-consistency is wanted. */

.article-body {
  max-width: 42rem;
  margin-inline: auto;
  font-family: "Iowan Old Style", "Palatino Linotype", Palatino, "Book Antiqua", Charter, Georgia, "Times New Roman", serif;
  font-size: 1.1875rem;        /* 19px — a clear, comfortable bump from 16px */
  line-height: 1.75;
  color: var(--color-zinc-800);
}

@media (prefers-color-scheme: dark) {
  .article-body { color: var(--color-neutral-200); }
}

.article-body > :first-child { margin-top: 0; }
.article-body > :last-child { margin-bottom: 0; }

/* Generous spacing between paragraphs (explicitly requested). */
.article-body p { margin-block: 1.4em; }

.article-body h1,
.article-body h2,
.article-body h3,
.article-body h4 {
  font-family: inherit;          /* serif headings: the piece speaks one voice */
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: -0.01em;
  margin-top: 2em;
  margin-bottom: 0.6em;
}
.article-body h1 { font-size: 2rem; }
.article-body h2 { font-size: 1.625rem; }
.article-body h3 { font-size: 1.3125rem; }
.article-body h4 { font-size: 1.125rem; }

.article-body a {
  color: var(--color-blue-700);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
}
.article-body a:hover { color: var(--color-blue-800); }

@media (prefers-color-scheme: dark) {
  .article-body a { color: var(--color-blue-300); }
  .article-body a:hover { color: var(--color-blue-200); }
}

.article-body strong { font-weight: 600; }
.article-body em { font-style: italic; }

.article-body ul,
.article-body ol {
  margin-block: 1.4em;
  padding-left: 1.5em;
}
.article-body ul { list-style: disc; }
.article-body ol { list-style: decimal; }
.article-body li { margin-block: 0.4em; }
.article-body li::marker { color: var(--color-zinc-400); }

.article-body blockquote {
  margin-block: 1.6em;
  padding-left: 1.25rem;
  border-left: 3px solid var(--color-zinc-300);
  font-style: italic;
  color: var(--color-zinc-600);
}

.article-body code {
  font-family: var(--font-mono);
  font-size: 0.9em;
  background: var(--color-zinc-100);
  padding: 0.1em 0.35em;
  border-radius: 0.25rem;
}
.article-body pre {
  font-family: var(--font-mono);
  margin-block: 1.6em;
  padding: 1rem 1.25rem;
  background: var(--color-zinc-50);
  border: 1px solid var(--color-zinc-200);
  border-radius: 0.5rem;
  overflow-x: auto;
  font-size: 0.95rem;
  line-height: 1.6;
}
.article-body pre code { background: none; padding: 0; }

/* Inline images pasted directly into the prose get breathing room.
   Excludes images inside an .article-embed (the filmstrip), which would
   otherwise inherit this margin and stack excess space beneath each image. */
.article-body img:not(.article-embed img) { margin-block: 1.6em; }

/* ========================================
   Article Embed Caption
   ========================================
   The attribution under an embedded work: the work's title as a quiet
   italic label (not a link), with a subtle "view work →" action below it.

   Scoped under .article-body to win specificity over the body's link
   styling (.article-body a = 0,1,1; these = 0,2,0) so the title never goes
   link-blue and the action stays muted instead of underlined. */

.article-body .article-embed-caption {
  margin-top: 1.5rem;
  text-align: center;
}

.article-body .article-embed-title {
  font-family: inherit;            /* serif, from .article-body */
  font-style: italic;              /* editorial convention for artwork titles */
  font-weight: 400;
  font-size: 1.0625rem;
  line-height: 1.4;
  color: var(--color-zinc-600);
  margin: 0 0 0.25rem;
}

.article-body .article-embed-action {
  display: inline-block;
  font-family: var(--font-sans);   /* sans, to contrast the serif body */
  font-size: 0.8rem;
  color: var(--color-zinc-400);
  text-decoration: none;
  transition: color 0.2s ease;
}
.article-body .article-embed-action:hover { color: var(--color-zinc-700); }

@media (prefers-color-scheme: dark) {
  .article-body .article-embed-title { color: var(--color-neutral-400); }
  .article-body .article-embed-action { color: var(--color-neutral-500); }
  .article-body .article-embed-action:hover { color: var(--color-neutral-300); }
}

@media (prefers-color-scheme: dark) {
  .article-body li::marker { color: var(--color-neutral-600); }
  .article-body blockquote {
    border-left-color: var(--color-neutral-700);
    color: var(--color-neutral-400);
  }
  .article-body code { background: var(--color-neutral-900); }
  .article-body pre {
    background: var(--color-neutral-950);
    border-color: var(--color-neutral-800);
  }
}