/*! PolyFishCSS-Effects.css | v1.0.0-rc.2-rc.1-rc.1-rc.1-rc.1-rc.1 | polyfish.org | MIT
 *
 * Visual effect utilities: transforms, filters, gradients, rings, animations,
 * scroll behaviour and the group/peer interaction patterns.
 *
 * Depends on: PolyFishCSS-Core.css (tokens, motion easing, accent roles).
 * Load order: after Core, with the other utility files at the end.
 *
 * CONTENTS
 *   1. Transforms
 *   2. Filters and backdrop
 *   3. Gradients
 *   4. Rings and outlines
 *   5. Animations
 *   6. Transition control
 *   7. Scroll behaviour
 *   8. Group and focus-within
 *   9. Blend, isolation and misc
 *
 * MOTION POLICY
 *   Every animation in this file is wrapped in prefers-reduced-motion. Core's
 *   global block already neutralises durations, but that leaves an element
 *   stuck at whatever the first keyframe said - so animations that start at
 *   opacity 0 are not merely slowed here, they are not applied at all.
 */

@layer polyfish.core, polyfish.grid, polyfish.headers, polyfish.content, polyfish.forms, polyfish.components, polyfish.footers, polyfish.space, polyfish.effects, polyfish.utilities, polyfish.fonts, polyfish.compat;

/* ==========================================================================
   CASCADE LAYER
   ==========================================================================
   Everything below sits in `polyfish.effects`. The layer order is declared in
   every module, so it holds no matter which order you link the files in.

   Why this matters to you: unlayered CSS beats layered CSS regardless of
   specificity. Your own stylesheet overrides any framework rule with a plain
   class selector — no `!important`, no specificity arms race, no worrying
   about whether Utilities loaded last.

     <link rel="stylesheet" href="PolyFishCSS-Core.min.css">
     <link rel="stylesheet" href="my-app.css">   <!-- wins, always -->

   If you want your CSS layered too, name a layer after the framework's:

     @layer polyfish, app;
     @layer app { .card { background: rebeccapurple; } }
   ========================================================================== */

@layer polyfish.effects {
  /* ==========================================================================
     #TRANSFORMS
     ==========================================================================
     Composed through custom properties rather than a single transform
     declaration, so translate, rotate and scale utilities can be combined on
     one element without the last one silently winning.
     ========================================================================== */
  
  .pf-transform,
  [class*="pf-translate-"],
  [class*="pf-rotate-"],
  [class*="pf-scale-"] {
    transform: translate(var(--pf-tx, 0), var(--pf-ty, 0)) rotate(var(--pf-rotate, 0deg))
      scale(var(--pf-scale, 1));
  }
  
  .pf-translate-x-1 { --pf-tx: 0.25rem; }
  .pf-translate-x-2 { --pf-tx: 0.5rem; }
  .pf-translate-x-4 { --pf-tx: 1rem; }
  .pf-translate-y-1 { --pf-ty: 0.25rem; }
  .pf-translate-y-2 { --pf-ty: 0.5rem; }
  .pf-translate-y-4 { --pf-ty: 1rem; }
  .pf-translate-y-up-1 { --pf-ty: -0.25rem; }
  .pf-translate-y-up-2 { --pf-ty: -0.5rem; }
  .pf-translate-center { --pf-tx: -50%; --pf-ty: -50%; }
  
  .pf-rotate-45 { --pf-rotate: 45deg; }
  .pf-rotate-90 { --pf-rotate: 90deg; }
  .pf-rotate-180 { --pf-rotate: 180deg; }
  .pf-rotate-back-45 { --pf-rotate: -45deg; }
  
  .pf-scale-95 { --pf-scale: 0.95; }
  .pf-scale-105 { --pf-scale: 1.05; }
  .pf-scale-110 { --pf-scale: 1.1; }
  
  .pf-flip-x { transform: scaleX(-1); }
  .pf-origin-top { transform-origin: top; }
  .pf-origin-bottom { transform-origin: bottom; }
  .pf-origin-left { transform-origin: left; }
  
  /* ==========================================================================
     #FILTERS AND BACKDROP
     ==========================================================================
     backdrop-filter is expensive on a large surface and on a phone it can cost
     more than it looks worth - use it on a bar or a panel, not a full page.
     ========================================================================== */
  
  .pf-blur-sm { filter: blur(2px); }
  .pf-blur { filter: blur(6px); }
  .pf-blur-lg { filter: blur(16px); }
  .pf-grayscale { filter: grayscale(1); }
  .pf-dim { filter: brightness(0.7); }
  .pf-bright { filter: brightness(1.15); }
  .pf-saturate { filter: saturate(1.3); }
  .pf-desaturate { filter: saturate(0.55); }
  .pf-filter-none { filter: none; }
  
  .pf-backdrop-blur {
    -webkit-backdrop-filter: blur(10px);
    backdrop-filter: blur(10px);
  }
  .pf-backdrop-blur-lg {
    -webkit-backdrop-filter: blur(20px);
    backdrop-filter: blur(20px);
  }
  
  /* A frosted panel that still works where backdrop-filter does not: the solid
     background is declared first and only replaced where the feature exists. */
  .pf-frost { background: var(--pf-bg-deep); }
  @supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .pf-frost {
      background: color-mix(in srgb, var(--pf-bg-deep) 72%, transparent);
      -webkit-backdrop-filter: blur(14px);
      backdrop-filter: blur(14px);
    }
  }
  
  /* ==========================================================================
     #GRADIENTS
     ==========================================================================
     Built from role tokens, so a gradient follows a theme instead of pinning
     the page to one palette.
     ========================================================================== */
  
  .pf-gradient-surface {
    background-image: linear-gradient(180deg, var(--pf-surface), transparent 70%);
  }
  .pf-gradient-deep {
    background-image: linear-gradient(180deg, var(--pf-bg-deep), var(--pf-bg));
  }
  .pf-gradient-accent {
    background-image: linear-gradient(120deg, var(--pf-accent), var(--pf-accent-2));
  }
  .pf-gradient-edge {
    background-image: linear-gradient(90deg, var(--pf-surface), transparent);
  }
  
  /**
   * Gradient text
   *
   * <h2 class="pf-gradient-text">Headline</h2>
   *
   * The fallback colour is declared first: background-clip: text with a
   * transparent fill produces invisible text where the clip is unsupported, and
   * invisible text is a worse outcome than a flat colour.
   */
  .pf-gradient-text { color: var(--pf-accent); }
  @supports (background-clip: text) or (-webkit-background-clip: text) {
    .pf-gradient-text {
      background-image: linear-gradient(100deg, var(--pf-accent), var(--pf-accent-2));
      -webkit-background-clip: text;
      background-clip: text;
      color: transparent;
    }
  }
  
  /* A hairline rule that fades at both ends. */
  .pf-rule-fade {
    height: 1px;
    border: 0;
    background-image: linear-gradient(90deg, transparent, var(--pf-border-strong), transparent);
  }
  
  /* ==========================================================================
     #RINGS AND OUTLINES
     ==========================================================================
     A ring is a box-shadow, so it follows border-radius and costs no layout.
     An offset ring needs two shadows: the inner one paints the gap in the page
     colour, which is why --pf-ring-offset-color exists.
     ========================================================================== */
  
  .pf-ring-1 { box-shadow: 0 0 0 1px var(--pf-ring-color, var(--pf-border-strong)); }
  .pf-ring-2 { box-shadow: 0 0 0 2px var(--pf-ring-color, var(--pf-border-strong)); }
  .pf-ring-accent { --pf-ring-color: var(--pf-accent); }
  .pf-ring-danger { --pf-ring-color: #e08f8f; }
  .pf-ring-offset {
    box-shadow: 0 0 0 2px var(--pf-ring-offset-color, var(--pf-bg)),
      0 0 0 4px var(--pf-ring-color, var(--pf-accent));
  }
  
  /* ==========================================================================
     #ANIMATIONS
     ==========================================================================
     Five named animations, all opt-in. Each is defined only inside the
     reduced-motion guard, so an element that starts invisible never gets
     stranded there when motion is turned off.
     ========================================================================== */
  
  @media (prefers-reduced-motion: no-preference) {
    .pf-animate-spin { animation: pf-spin 1s linear infinite; }
    .pf-animate-pulse { animation: pf-pulse 2s var(--pf-ease) infinite; }
    .pf-animate-bounce { animation: pf-bounce 1s infinite; }
    .pf-animate-fade { animation: pf-fade-in 400ms var(--pf-ease) both; }
    .pf-animate-rise { animation: pf-rise 400ms var(--pf-ease) both; }
    .pf-animate-slide-in { animation: pf-slide-in 300ms var(--pf-ease) both; }
  
    .pf-delay-1 { animation-delay: 80ms; }
    .pf-delay-2 { animation-delay: 160ms; }
    .pf-delay-3 { animation-delay: 240ms; }
    .pf-delay-4 { animation-delay: 320ms; }
  }
  
  @keyframes pf-pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.55; }
  }
  @keyframes pf-bounce {
    0%, 100% { transform: translateY(-15%); animation-timing-function: cubic-bezier(0.8, 0, 1, 1); }
    50% { transform: none; animation-timing-function: cubic-bezier(0, 0, 0.2, 1); }
  }
  @keyframes pf-rise {
    from { opacity: 0; transform: translateY(0.75rem); }
    to { opacity: 1; transform: none; }
  }
  @keyframes pf-slide-in {
    from { opacity: 0; transform: translateX(-0.75rem); }
    to { opacity: 1; transform: none; }
  }
  
  /* ==========================================================================
     #TRANSITION CONTROL
     ========================================================================== */
  
  .pf-duration-fast { transition-duration: 90ms; }
  .pf-duration-slow { transition-duration: 320ms; }
  .pf-duration-slower { transition-duration: 600ms; }
  .pf-ease-out { transition-timing-function: cubic-bezier(0, 0, 0.2, 1); }
  .pf-ease-in { transition-timing-function: cubic-bezier(0.4, 0, 1, 1); }
  .pf-ease-spring { transition-timing-function: cubic-bezier(0.34, 1.56, 0.64, 1); }
  .pf-transition-transform { transition: transform var(--pf-speed) var(--pf-ease); }
  .pf-transition-opacity { transition: opacity var(--pf-speed) var(--pf-ease); }
  .pf-transition-shadow { transition: box-shadow var(--pf-speed) var(--pf-ease); }
  
  /* ==========================================================================
     #SCROLL BEHAVIOUR
     ========================================================================== */
  
  .pf-snap-x { scroll-snap-type: x mandatory; overscroll-behavior-x: contain; }
  .pf-snap-y { scroll-snap-type: y mandatory; overscroll-behavior-y: contain; }
  .pf-snap-start { scroll-snap-align: start; }
  .pf-snap-center { scroll-snap-align: center; }
  .pf-snap-none { scroll-snap-align: none; }
  .pf-scroll-smooth { scroll-behavior: smooth; }
  .pf-scroll-contain { overscroll-behavior: contain; }
  .pf-scrollbar-thin { scrollbar-width: thin; }
  .pf-scrollbar-none { scrollbar-width: none; }
  .pf-scrollbar-none::-webkit-scrollbar { display: none; }
  .pf-scroll-mt { scroll-margin-top: 6rem; }
  
  /* ==========================================================================
     #GROUP AND FOCUS-WITHIN
     ==========================================================================
     The pattern a utility framework needs and CSS has no shorthand for: a child
     reacting to the parent's state.
     ========================================================================== */
  
  /**
   * <article class="pf-group pf-card">
   *   <h3>Title</h3>
   *   <span class="pf-group-show">→</span>
   * </article>
   *
   * The reveal is scoped to (hover: hover) so a touch device shows the affordance
   * permanently rather than hiding it behind a state a finger cannot enter.
   */
  .pf-group-show { opacity: 1; }
  .pf-group-hide { opacity: 0; }
  
  @media (hover: hover) {
    .pf-group .pf-group-show {
      opacity: 0;
      transition: opacity var(--pf-speed) var(--pf-ease), transform var(--pf-speed) var(--pf-ease);
    }
    .pf-group:hover .pf-group-show,
    .pf-group:focus-within .pf-group-show { opacity: 1; }
    .pf-group:hover .pf-group-hide { opacity: 0; }
    .pf-group:hover .pf-group-slide { transform: translateX(0.25rem); }
    .pf-group-slide { transition: transform var(--pf-speed) var(--pf-ease); }
  }
  
  /* Focus-within, which is how a card containing a link announces itself to a
     keyboard user the way hover does to a mouse. */
  .pf-focus-ring:focus-within { box-shadow: var(--pf-ring); }
  .pf-focus-border:focus-within { border-color: var(--pf-accent); }
  
  /* ==========================================================================
     #BLEND, ISOLATION AND MISC
     ========================================================================== */
  
  .pf-isolate { isolation: isolate; }
  .pf-blend-multiply { mix-blend-mode: multiply; }
  .pf-blend-screen { mix-blend-mode: screen; }
  .pf-blend-overlay { mix-blend-mode: overlay; }
  
  .pf-will-change-transform { will-change: transform; }
  .pf-will-change-opacity { will-change: opacity; }
  
  .pf-resize-none { resize: none; }
  .pf-resize-y { resize: vertical; }
  .pf-appearance-none { appearance: none; }
  .pf-accent-color { accent-color: var(--pf-accent); }
  .pf-caret-accent { caret-color: var(--pf-accent); }
  .pf-list-none { list-style: none; }
  .pf-list-disc { list-style: disc; padding-inline-start: 1.25rem; }
  .pf-list-decimal { list-style: decimal; padding-inline-start: 1.4rem; }
  
  /* Mask the bottom of an overflowing block so the cut looks intentional. */
  .pf-fade-bottom {
    -webkit-mask-image: linear-gradient(180deg, #000 70%, transparent);
    mask-image: linear-gradient(180deg, #000 70%, transparent);
  }
  .pf-fade-right {
    -webkit-mask-image: linear-gradient(90deg, #000 80%, transparent);
    mask-image: linear-gradient(90deg, #000 80%, transparent);
  }
}
