/*
Theme Name: Savepoint
Theme URI: https://savepoint-studios.com/
Author: Vonkova
Description: Dark, motion-led BLOCK theme for Savepoint Studios, built around the XMB 360 dashboard's own palette and typography. Every section is a block pattern and every template is editable in Appearance > Editor. Designed for bbPress forums and BuddyPress profiles, friends, messaging and activity walls.
Version: 0.3.2
Requires at least: 6.5
Tested up to: 6.8
Requires PHP: 7.4
License: GPL-2.0-or-later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Text Domain: savepoint
Tags: block-patterns, full-site-editing, dark, one-column, custom-colors, forum, community
*/

/* ===========================================================================
   WHAT IS AND IS NOT IN THIS FILE

   NOT here: colours, font sizes, spacing steps, block defaults, layout widths.
   All of that is theme.json, which is what makes WordPress show it in the
   editor as a real choice rather than burying it where only a file can reach.
   If you want to change a colour, change it in theme.json or in Appearance >
   Editor > Styles - not here.

   Here: the handful of things CSS can do and blocks cannot. Motion, mostly -
   the drifting wave, the mote field, the headline sheen, the hover lift, the
   scroll reveal - plus the few structural fixes a dark community site needs.
   Every rule below is attached to a class that a pattern or a template part
   puts on an ordinary block, so the block stays editable and the effect comes
   along for free.
   =========================================================================== */

:root {
  /* Short aliases for the theme.json presets, so the rules below read. These
     are the ONLY colour declarations in this file and every one of them points
     at a preset rather than a hex. */
  --sp-deep:   var(--wp--preset--color--deep);
  --sp-ground: var(--wp--preset--color--ground);
  --sp-raised: var(--wp--preset--color--raised);
  --sp-title:  var(--wp--preset--color--title);
  --sp-desc:   var(--wp--preset--color--muted);
  --sp-accent: var(--wp--preset--color--amber);
  --sp-cool:   var(--wp--preset--color--cool);
  --sp-rail:      var(--wp--preset--color--rail);
  --sp-rail-soft: var(--wp--preset--color--rail-soft);
  --sp-display: var(--wp--preset--font-family--display);
  --sp-body:    var(--wp--preset--font-family--body);
}

/* Focus has to be visible on a dark ground. A block theme does not give you
   this and a site without it is unusable by keyboard. */
:where(a, button, input, textarea, select, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--sp-accent);
  outline-offset: 3px;
  border-radius: 2px;
}

/* ===========================================================================
   MOTION  (all of it behind prefers-reduced-motion at the end of this block)
   =========================================================================== */
@keyframes sp-drift {
  0%   { transform: translate3d(-6%, 0, 0) scaleY(1);      opacity: 0.55; }
  50%  { transform: translate3d(4%, -14px, 0) scaleY(1.18); opacity: 0.80; }
  100% { transform: translate3d(-6%, 0, 0) scaleY(1);      opacity: 0.55; }
}
@keyframes sp-rise {
  from { opacity: 0; transform: translate3d(0, 22px, 0); }
  to   { opacity: 1; transform: none; }
}
@keyframes sp-sweep {
  from { background-position: -140% 0; }
  to   { background-position: 240% 0; }
}
@keyframes sp-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(255, 214, 158, 0.28); }
  50%      { box-shadow: 0 0 0 14px rgba(255, 214, 158, 0); }
}
@keyframes sp-mote {
  0%   { transform: translate3d(0, 10vh, 0) scale(0.9); opacity: 0; }
  14%  { opacity: var(--mo, 0.22); }
  86%  { opacity: var(--mo, 0.22); }
  100% { transform: translate3d(var(--mx, 0px), -104vh, 0) scale(1.12); opacity: 0; }
}

/* ---- the wave -------------------------------------------------------------
   Drawn as two pseudo-elements on whatever block carries .sp-hero or .sp-cta,
   so a pattern needs no extra markup for it and nobody can delete half of it
   in the editor by accident. This IS the XMB's own motion, not decoration. */
.sp-hero, .sp-cta { position: relative; overflow: hidden; isolation: isolate; }
.sp-hero::before, .sp-hero::after,
.sp-cta::before {
  content: ""; position: absolute; left: -10%; right: -10%;
  height: 320px; border-radius: 50%; filter: blur(44px);
  pointer-events: none; z-index: -1;
  animation: sp-drift 14s ease-in-out infinite;
}
.sp-hero::before {
  top: 90px;
  background: radial-gradient(60% 100% at 50% 50%, rgba(255,214,158,0.50), rgba(255,214,158,0) 70%);
}
.sp-hero::after {
  top: 210px; animation-duration: 19s; animation-direction: reverse;
  background: radial-gradient(60% 100% at 50% 50%, rgba(143,199,232,0.34), rgba(143,199,232,0) 70%);
}
.sp-cta::before {
  top: 30px; animation-duration: 17s; opacity: 0.6;
  background: radial-gradient(60% 100% at 50% 50%, rgba(255,214,158,0.30), rgba(255,214,158,0) 70%);
}

/* ---- the hero's staggered entrance ---------------------------------------
   Applied to the pattern's own children by position, so adding or removing a
   block inside the hero cannot leave an orphaned delay class behind. */
.sp-hero > * { animation: sp-rise 820ms cubic-bezier(.22,.68,0,1) both; }
.sp-hero > :nth-child(2) { animation-delay:  90ms; }
.sp-hero > :nth-child(3) { animation-delay: 180ms; }
.sp-hero > :nth-child(4) { animation-delay: 270ms; }
.sp-hero > :nth-child(5) { animation-delay: 360ms; }

/* ---- the sheen on a run of headline text --------------------------------- */
.sp-shine {
  background-image: linear-gradient(100deg, transparent 20%, rgba(255,244,230,0.5) 50%, transparent 80%);
  background-size: 200% 100%;
  -webkit-background-clip: text; background-clip: text; color: transparent;
  animation: sp-sweep 5.5s linear infinite;
}

/* ---- a card that answers the cursor -------------------------------------- */
.sp-lift { transition: transform 260ms cubic-bezier(.22,.68,0,1), border-color 260ms ease; }
.sp-lift:hover { transform: translateY(-6px); border-color: var(--sp-rail) !important; }

/* ---- the button pulse ---------------------------------------------------- */
.sp-btn--pulse .wp-block-button__link { animation: sp-pulse 3.4s ease-out infinite; }

/* ---- the mote field ------------------------------------------------------
   Printed by savepoint_motes() on wp_body_open. Fixed rather than
   per-section, so it reads as air in the room rather than an effect bolted to
   one band.

   THE TRAP, and it cost a round: a position:fixed element with z-index 0 paints
   ABOVE every non-positioned block, because un-positioned boxes sit in a lower
   layer than any positioned one. "z-index: 0" is not "at the back" - it is "at
   the back of the positioned things". So the field is at -1 and the content is
   left alone, which is the version that cannot be broken by adding a section. */
.sp-motes {
  position: fixed; inset: 0; z-index: -1;
  pointer-events: none; overflow: hidden; contain: strict;
}
.sp-mote {
  position: absolute; bottom: -8vh; border-radius: 50%;
  background: var(--sp-accent);
  animation-name: sp-mote; animation-timing-function: linear;
  animation-iteration-count: infinite;
  will-change: transform, opacity;
}
.sp-mote--cool { background: var(--sp-cool); }
.sp-mote--soft { filter: blur(1.5px); }

/* ---- scroll reveal -------------------------------------------------------
   main.js adds .is-in when the element enters the viewport. With no JS the
   element is simply visible, which is the right failure, so .no-js wins. */
.sp-reveal { opacity: 0; transform: translate3d(0, 26px, 0); transition: opacity 700ms ease, transform 700ms cubic-bezier(.22,.68,0,1); }
.sp-reveal.is-in { opacity: 1; transform: none; }
.no-js .sp-reveal { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .sp-hero > *, .sp-hero::before, .sp-hero::after, .sp-cta::before,
  .sp-shine, .sp-lift, .sp-btn--pulse .wp-block-button__link {
    animation: none !important;
    transition: none !important;
  }
  .sp-shine { color: var(--sp-accent); }
  .sp-reveal { opacity: 1; transform: none; transition: none; }
  /* Motion and nothing else, so it goes entirely rather than freezing as two
     dozen dots scattered over the page. */
  .sp-motes { display: none; }
}

/* ===========================================================================
   LAYOUT AND SECTIONS
   ---------------------------------------------------------------------------
   The patterns carry className and nothing else - see any patterns/*.php for
   why - so every measurement in the design lives here. That is also what makes
   the blocks safe to edit: WordPress re-generates a static block's markup from
   its attributes, and attributes it did not write are attributes it can
   disagree with.
   =========================================================================== */

.sp-hero    { padding-block: 6.75rem 5.25rem; }
.sp-section { padding-block: 5.25rem; }
.sp-stats   { padding-block: 5.25rem 0; }
.sp-cta     { padding-block: 6.75rem; border-top: 1px solid var(--sp-rail-soft); }
.sp-gallery {
  padding-block: 5.25rem;
  background: var(--sp-ground);
  border-top: 1px solid var(--sp-rail-soft);
  border-bottom: 1px solid var(--sp-rail-soft);
}
.sp-body    { padding-block: 3rem 5.25rem; }

/* ---- the hero ------------------------------------------------------------ */
.sp-eyebrow {
  font-size: 0.75rem; letter-spacing: 0.28em; text-transform: uppercase;
  color: var(--sp-accent); margin-block: 0 1.5rem;
}
.sp-hero__title { margin-block: 0; }
/* The two headline lines are two blocks, so the second can carry the sheen
   without a span inside a Heading - which is markup a Heading will not keep. */
.sp-hero__title + .sp-hero__title { margin-top: 0; }
.sp-hero__lead {
  font-size: clamp(1.0625rem, 2vw, 1.25rem); line-height: 1.6;
  color: var(--sp-desc); max-width: 50ch; margin-block: 1.75rem 2.5rem;
}
.sp-hero__cta { margin-bottom: 4.25rem; gap: 0.875rem; }

/* ---- section headings ---------------------------------------------------- */
.sp-section__title { margin-block: 0 0.875rem; }
.sp-section__lead {
  font-size: 1.0625rem; line-height: 1.7; color: var(--sp-desc);
  max-width: 56ch; margin-block: 0 3rem;
}
.sp-cta .sp-section__lead { margin-inline: auto; max-width: 54ch; }

/* ---- statistics ---------------------------------------------------------- */
.sp-stats__row { gap: 2rem; }
.sp-stat { border-top: 1px solid var(--sp-rail); padding-top: 1.375rem; }
.sp-stat__num {
  font-size: clamp(2.25rem, 5vw, 3.375rem); font-weight: 900; line-height: 1;
  margin-block: 0 0.375rem; font-variant-numeric: tabular-nums;
  color: var(--sp-title);
}
.sp-stat__label {
  font-size: 0.8125rem; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--sp-desc); margin-block: 0;
}

/* ---- cards --------------------------------------------------------------- */
.sp-bento { gap: 1.25rem; margin-bottom: 1.25rem; }
.sp-card {
  background: var(--sp-ground);
  border: 1px solid var(--sp-rail-soft); border-radius: 10px;
  padding: 2.125rem; height: 100%;
}
.sp-card > :first-child { margin-top: 0; }
.sp-card > :last-child  { margin-bottom: 0; }
.sp-card--glow {
  border-color: var(--sp-rail);
  background: linear-gradient(135deg, rgba(255,214,158,0.08), rgba(255,214,158,0));
}
/* The split card puts its image beside the words at a comfortable width and
   stacks it underneath when there is not room. */
.sp-card--split { display: flex; flex-wrap: wrap; gap: 0 1.875rem; align-items: center; }
.sp-card--split > :where(h3, p) { flex: 999 1 16rem; min-width: 0; margin-block: 0.4rem; }
.sp-card--split > .sp-slot      { flex: 1 1 11rem; min-width: 0; }

/* ---- the gallery grid ---------------------------------------------------- */
/* Two rows of two. Each row is a Columns block, so somebody can add a third
   slot to a row in the editor and it simply divides the row again. */
.sp-shots { gap: 1.25rem; }
.sp-shots + .sp-shots { margin-top: 1.25rem; }

/* ===========================================================================
   PHOTO SLOTS
   ---------------------------------------------------------------------------
   A slot is a GROUP that holds the box - the ratio, the radius, the hatch - with
   an ordinary core Image block inside it. That split is deliberate and it is
   what makes the slots usable:

     - the Group gives the space a shape before there is any picture, so the
       layout never jumps when one is added;
     - the Image block inside is a plain, file-less Image block, which is what
       puts a real Upload / Media Library placeholder in the editor. The earlier
       version wrote the <figure> and the <img> by hand, and an Image block that
       already has HTML looks FILLED to the editor - which is why there was no
       way to upload into any of these.

   The ratio lives here rather than as a block attribute so that nothing in the
   markup can disagree with what WordPress would regenerate.
   =========================================================================== */
.sp-slot {
  position: relative;
  border-radius: 10px;
  margin-block: 0;
}

/* Empty: hatch, border, and a shape to occupy. */
.sp-slot:not(:has(img[src])) {
  background-color: var(--sp-ground);
  background-image: linear-gradient(135deg,
    rgba(255,244,230,0.045) 25%, transparent 25%, transparent 50%,
    rgba(255,244,230,0.045) 50%, rgba(255,244,230,0.045) 75%, transparent 75%);
  background-size: 14px 14px;
  border: 1px solid var(--sp-rail-soft);
}

/* Filled: clip the picture to the radius. NOT while empty, or the editor's own
   upload placeholder and its toolbar get cut off at the box's edge. */
.sp-slot:has(img[src]) { overflow: hidden; }

/* The image block inside fills the box completely. Core gives a figure its own
   margins; a slot is the frame, so they go. */
.sp-slot .wp-block-image,
.sp-slot figure {
  margin: 0;
  width: 100%;
  height: 100%;
}
.sp-slot img { display: block; width: 100%; height: 100%; object-fit: cover; }

/* The shapes. */
.sp-slot--hero { aspect-ratio: 16 / 9; box-shadow: 0 50px 120px rgba(0,0,0,0.6); }
.sp-slot--wide { aspect-ratio: 16 / 9; }
.sp-slot--box  { aspect-ratio: 3 / 4; border-radius: 6px; }
.sp-slot--tall { aspect-ratio: 4 / 5; }

/* An empty slot has no content, so aspect-ratio alone would collapse it in some
   layouts. This gives it something to be tall about. */
.sp-slot:not(:has(img[src]))::after { content: ""; display: block; }

/* In the editor the placeholder needs a usable minimum even inside the ratio. */
.editor-styles-wrapper .sp-slot { min-height: 8rem; }

/* ===========================================================================
   PAGE HEADER BAND, and the body under it
   =========================================================================== */
.sp-pagehead {
  background: var(--sp-ground);
  border-bottom: 1px solid var(--sp-rail-soft);
  padding-block: 3rem 2rem;
}
.sp-pagehead__title { margin-block: 0; font-size: clamp(2rem, 4.2vw, 2.75rem); }
.sp-meta { font-size: 0.8125rem; color: var(--sp-desc); gap: 0.75rem; }
.sp-meta a { color: var(--sp-desc); }
.sp-community { max-width: none; }

/* ---- the blog grid, search results, comments ----------------------------- */
.sp-feed .wp-block-post-template { gap: 1.25rem; }
.sp-result { border-top: 1px solid var(--sp-rail-soft); padding-block: 1.5rem; }
.sp-comment { border-top: 1px solid var(--sp-rail-soft); padding-block: 1.5rem; }
.sp-comments { margin-top: 5.25rem; }
.sp-pages { margin-top: 3rem; gap: 0.625rem; }
.sp-pages .page-numbers,
.wp-block-query-pagination a,
.wp-block-comments-pagination a {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 44px; min-height: 44px; padding-inline: 0.875rem;
  border: 1px solid var(--sp-rail-soft); border-radius: 6px;
  color: var(--sp-desc); font-size: 0.875rem;
}
.sp-pages .page-numbers.current {
  background: var(--sp-accent); border-color: var(--sp-accent);
  color: var(--sp-deep); font-weight: 600;
}

/* ===========================================================================
   HEADER AND FOOTER
   =========================================================================== */
.sp-head__inner { gap: 1.375rem; padding-block: 1rem; }
.sp-head { border-bottom: 1px solid var(--sp-rail-soft); }

/* The amber mark beside the site name, drawn rather than placed - a Spacer
   block for it was one more thing to get out of step, and one more empty block
   in the editor's list view for somebody to drag away by accident. */
.sp-logo { display: flex; align-items: center; gap: 0.7rem; }
.sp-logo::before {
  content: ""; width: 11px; height: 26px; flex-shrink: 0;
  background: var(--sp-accent); border-radius: 1px;
}
.sp-logo a { color: var(--sp-title); }
.sp-head__actions { margin-left: auto; gap: 0.625rem; }
.sp-head__donate .wp-block-button__link { padding: 0.75rem 1.375rem; font-size: 0.875rem; }

.sp-foot { background: var(--sp-ground); border-top: 1px solid var(--sp-rail-soft); padding-block: 2.75rem; }
.sp-foot__inner { gap: 1.5rem; }
.sp-foot__note { font-size: 0.8125rem; color: var(--sp-desc); margin: 0; flex: 999 1 18rem; }
.sp-foot__links { font-size: 0.8125rem; }

/* ===========================================================================
   NARROW SCREENS
   =========================================================================== */
@media (max-width: 781px) {
  .sp-hero  { padding-block: 4rem 3.5rem; }
  .sp-section, .sp-gallery, .sp-cta { padding-block: 3.5rem; }
  .sp-stats { padding-block: 3.5rem 0; }
  .sp-card  { padding: 1.5rem; }
  .sp-head__actions { margin-left: 0; }
}
