/*! PolyFishCSS-Core.css | v1.0.0-rc.2-rc.1-rc.1-rc.1-rc.1-rc.1 | polyfish.org | MIT
 *
 * Required base layer: design tokens, reset, typography, layout primitives,
 * buttons and focus handling. Every other module reads the custom properties
 * declared here, so this file always ships.
 *
 * Depends on: nothing.
 * Load order: Core -> Headers -> Content -> Footers -> Utilities.
 *             Utilities must come last; several of its helpers rely on being
 *             the later rule when specificity ties with a component.
 *
 * CONTENTS
 *   1. Tokens
 *   2. Reset
 *   3. Typography
 *   4. Layout primitives
 *   5. Buttons
 *   6. Pills, markers, focus
 *
 * CONVENTIONS
 *   .pf-block            component root
 *   .pf-block__element   part of a component, never used alone
 *   .pf-block--modifier  variant, always applied alongside the root class
 *
 *   Components read role tokens (--pf-accent, --pf-surface, --pf-border) and
 *   never raw palette values, which is what makes a theme a handful of
 *   redeclared properties rather than a fork. See docs/tokens for the list.
 *
 * BROWSER BASELINE
 *   Custom properties, grid, gap, clamp(), clip-path, :focus-visible.
 *   Roughly Chrome 105 / Safari 15.4 / Firefox 103 and newer. Later features
 *   (color-mix, animation-timeline) are used only behind @supports or with a
 *   declared fallback, so nothing breaks on older engines - it just does less.
 *
 * EDITING THIS FILE
 *   Change values in section 1 and leave sections 2-6 alone. If you find
 *   yourself editing a component rule to change a colour, size or radius, the
 *   token you want almost certainly exists.
 */

@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.core`. 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.core {
  /* Fonts are loaded with a <link> in <head> — faster than an @import, which
   * blocks this stylesheet. Uncomment the line below if you want Core to be
   * self-contained and are not adding the link tag yourself. */
  /* @import url("https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,400..800&family=IBM+Plex+Mono:wght@400;500&family=IBM+Plex+Sans:wght@400;500;600&display=swap"); */
  
  /* ==========================================================================
     #TOKENS
     ========================================================================== */
  
  /**
   * Two layers, on purpose.
   *
   * The palette below is raw material: named colours with no opinion about
   * where they are used. The roles that follow map those colours onto jobs -
   * background, surface, text, accent, border. Components only ever reference
   * roles.
   *
   * That indirection is the whole theming story: redeclare eleven roles in a
   * stylesheet loaded after this one and every module follows, at runtime, with
   * no build step. Redeclaring the palette instead gives you a global shift;
   * redeclaring roles on a wrapper element gives you a scoped one.
   */
  
  :root {
    /* ---------------------------------------------------------------------
       PALETTE — raw colour with no opinion about where it is used.
       Named by position on a scale, never by hue: a token called --pf-blue-800
       holding #f4f7fa in a light theme is a name that lies, and every theme but
       the default made it lie. The neutral ramp runs 50 (lightest) to 950
       (darkest); the two accent hues are numbered, not coloured.
       --------------------------------------------------------------------- */
    --pf-base-900: #00030f;
    --pf-base-800: #000823;
    --pf-base-700: #000f3f;
    --pf-base-600: #17306e;
  
    /* Brand. The mark is a violet body with teal fins, and those two colours
       are deliberately NOT the interface accent: the logo should stay itself
       when a project retints the framework. Named here so brand moments can
       reach for them on purpose instead of hardcoding the hex. */
    --pf-brand: #7c3aed;
    --pf-brand-2: #2dd4bf;
    --pf-brand-deep: #2e1065;
  
    /* Accent hues. --pf-hue-1 is the primary voice, --pf-hue-2 the second.
       A theme redefines these; nothing in the framework assumes they are green
       and apricot, which is exactly why they are not called that. */
    --pf-hue-1: #8fe06a;
    --pf-hue-1-deep: #6dc44a;
    --pf-hue-1-wash: rgba(143, 224, 106, 0.12);
  
    --pf-hue-2: #e5b189;
    --pf-hue-2-deep: #c48f66;
    --pf-hue-2-wash: rgba(229, 177, 137, 0.12);
  
    --pf-base-50: #e9eefa;
    --pf-base-300: #a9bcd8;
    --pf-base-950: #00081f;
  
    /* Roles — override these, not the palette, when reskinning. */
    --pf-bg: var(--pf-base-800);
    --pf-bg-deep: var(--pf-base-900);
    --pf-surface: rgba(255, 255, 255, 0.045);
    --pf-surface-raised: rgba(255, 255, 255, 0.08);
    --pf-text: var(--pf-base-50);
    --pf-text-muted: var(--pf-base-300);
    --pf-accent: var(--pf-hue-1);
    --pf-accent-2: var(--pf-hue-2);
    --pf-border: rgba(169, 188, 216, 0.22);
    --pf-border-strong: rgba(143, 224, 106, 0.42);
    --pf-focus: var(--pf-hue-1);
  
    /* Type */
    --pf-font-display: "Bricolage Grotesque", "Bricolage Fallback", "Trebuchet MS", system-ui, sans-serif;
    --pf-font-body: "Inter", "Inter Fallback", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
    --pf-font-mono: "IBM Plex Mono", "Plex Mono Fallback", ui-monospace, SFMono-Regular, Menlo, monospace;
  
    /* Every step moved up. The smallest size in the framework is 13.6px rather
       than 12px, because 12px legal text is a decision to have it not read. */
    --pf-text-xs: 0.8125rem;
    --pf-text-sm: 0.9375rem;
    --pf-text-base: 1rem;
    --pf-text-lg: 1.1875rem;
    --pf-text-xl: 1.5rem;
    --pf-text-2xl: 1.9375rem;
    --pf-text-3xl: clamp(2.25rem, 1.65rem + 1.9vw, 3rem);
    --pf-text-4xl: clamp(2.75rem, 1.85rem + 3.4vw, 4.4rem);
    --pf-text-5xl: clamp(3.25rem, 1.75rem + 6.2vw, 6rem);
  
    /* Line height is the single largest readability lever after size. 1.7 on
       body copy, tightening as type grows, because a 60px headline at 1.7
       leaves its lines unrelated to each other. */
    --pf-leading-tight: 1.06;
    --pf-leading-snug: 1.22;
    --pf-leading: 1.7;
    --pf-leading-prose: 1.75;
  
    /* 66 characters. The research range for comfortable reading is roughly
       45-75; 66 sits where a long line still returns cleanly to the next. */
    --pf-measure: 66ch;
  
    /* Space — 4px base, doubling at the top end */
    --pf-space-1: 0.25rem;
    --pf-space-2: 0.5rem;
    --pf-space-3: 0.75rem;
    --pf-space-4: 1rem;
    --pf-space-5: 1.5rem;
    --pf-space-6: 2rem;
    --pf-space-7: 3.5rem;
    --pf-space-8: 5.5rem;
    --pf-space-9: 8.5rem;
  
    /* Gaps between grid cells and between a section head and its content. Both
       are their own tokens so density can be tuned without touching the space
       scale that padding depends on. */
    --pf-gap: clamp(1.5rem, 3vw, 2.5rem);
    --pf-gap-lg: clamp(2.5rem, 5.5vw, 4.5rem);
  
    /* Shape — one small radius, one large, one pill. Angles do the rest. */
    --pf-radius-sm: 6px;
    --pf-radius: 12px;
    --pf-radius-lg: 22px;
    --pf-radius-pill: 999px;
    --pf-cut: 18px; /* clipped corner depth for .pf-facet surfaces */
  
    --pf-ring: 0 0 0 3px rgba(143, 224, 106, 0.35);
    /* One shadow was too few once cards, menus and dialogs all needed depth.
       Three steps, all tuned for a dark page: shadows there read as an absence
       of light rather than a grey smear. */
    --pf-shadow-sm: 0 1px 2px rgba(0, 4, 18, 0.4);
    --pf-shadow: 0 8px 20px -12px rgba(0, 4, 18, 0.75);
    --pf-lift: 0 18px 40px -28px rgba(0, 6, 40, 0.9);
  
    /* Vertical rhythm between sections. Raise it for a more spacious site;
       .pf-section--roomy and --vast do exactly that, locally. */
    --pf-section-pad: clamp(5rem, 11vw, 9.5rem);
    --pf-container: 1160px;
    /* Gutters grow with the viewport and never drop below a thumb's width. */
    --pf-gutter: clamp(1.25rem, 5vw, 3rem);
    --pf-speed: 160ms;
    --pf-ease: cubic-bezier(0.2, 0.7, 0.3, 1);
  
    /* The scale mesh. Used by hero, CTA bands and facet surfaces. */
    --pf-mesh: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='72' height='62' viewBox='0 0 72 62'%3E%3Cpath d='M36 1 71 61H1Z' fill='none' stroke='%238fe06a' stroke-opacity='.16' stroke-width='1.4'/%3E%3C/svg%3E");
  
    color-scheme: dark;
  }
  
  /**
   * Automatic light mode
   *
   * The framework ships dark. A site opts into following the operating system
   * by leaving <html> unmarked, and opts out by declaring the theme:
   *
   *   <html lang="en">                     follows the OS
   *   <html lang="en" data-pf-theme="dark">  always dark
   *   <html lang="en" data-pf-theme="light"> always light
   *
   * Only the palette is redeclared. Every role, and therefore every component,
   * follows without a single component rule being touched - which is the whole
   * argument for the two-layer token structure in the first place.
   *
   * The accent darkens in light mode rather than staying put: a pastel green
   * that reads at 6.7:1 on deep blue reads at 1.6:1 on near-white. A palette
   * that does not change with the background is not a palette, it is a
   * decoration.
   *
   * polyfish.org itself sets data-pf-theme="dark", because its identity is the
   * dark page. The capability is here for your projects.
   */
  @media (prefers-color-scheme: light) {
    :root:not([data-pf-theme="dark"]) {
    color-scheme: light;
    --pf-base-900: #e7edf3;
    --pf-base-800: #f4f7fa;
    --pf-base-700: #d6e0e9;
    --pf-base-600: #b9c8d6;
    --pf-hue-1: #45702f;
    --pf-hue-1-deep: #35591f;
    --pf-hue-1-wash: rgba(69, 112, 47, 0.10);
    --pf-hue-2: #9a5620;
    --pf-hue-2-deep: #7d4415;
    --pf-hue-2-wash: rgba(154, 86, 32, 0.10);
    --pf-base-50: #0d2436;
    --pf-base-300: #3f5568;
    --pf-base-950: #f7fbff;
    --pf-surface: rgba(13, 36, 54, 0.045);
    --pf-surface-raised: rgba(13, 36, 54, 0.085);
    --pf-border: rgba(13, 36, 54, 0.16);
    --pf-border-strong: rgba(69, 112, 47, 0.45);
    --pf-mesh: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='72' height='62' viewBox='0 0 72 62'%3E%3Cpath d='M36 1 71 61H1Z' fill='none' stroke='%2345702f' stroke-opacity='.18' stroke-width='1.4'/%3E%3C/svg%3E");
    }
  }
  
  [data-pf-theme="light"] {
    color-scheme: light;
    --pf-base-900: #e7edf3;
    --pf-base-800: #f4f7fa;
    --pf-base-700: #d6e0e9;
    --pf-base-600: #b9c8d6;
    --pf-hue-1: #45702f;
    --pf-hue-1-deep: #35591f;
    --pf-hue-1-wash: rgba(69, 112, 47, 0.10);
    --pf-hue-2: #9a5620;
    --pf-hue-2-deep: #7d4415;
    --pf-hue-2-wash: rgba(154, 86, 32, 0.10);
    --pf-base-50: #0d2436;
    --pf-base-300: #3f5568;
    --pf-base-950: #f7fbff;
    --pf-surface: rgba(13, 36, 54, 0.045);
    --pf-surface-raised: rgba(13, 36, 54, 0.085);
    --pf-border: rgba(13, 36, 54, 0.16);
    --pf-border-strong: rgba(69, 112, 47, 0.45);
    --pf-mesh: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='72' height='62' viewBox='0 0 72 62'%3E%3Cpath d='M36 1 71 61H1Z' fill='none' stroke='%2345702f' stroke-opacity='.18' stroke-width='1.4'/%3E%3C/svg%3E");
  }
  
  /**
   * Light island
   *
   * <section class="pf-invert"> ... </section>
   *
   * Flips the roles to a light palette for one subtree. Put it on <body> for a
   * light site. The doubled class below raises specificity to (0,2,0) so the
   * flip beats a component's own background - .pf-card is (0,1,0) and is
   * declared later in the cascade, so a single class would lose.
   */
  .pf-invert {
    --pf-bg: var(--pf-base-50);
    --pf-bg-deep: #e4eaf9;
    --pf-surface: rgba(0, 15, 63, 0.05);
    --pf-surface-raised: rgba(0, 15, 63, 0.09);
    --pf-text: var(--pf-base-950);
    --pf-text-muted: #3d4e86;
    --pf-border: rgba(0, 15, 63, 0.18);
    --pf-border-strong: rgba(0, 15, 63, 0.4);
  }
  /* Doubled class so the flip beats a component's own background/colour. */
  .pf-invert.pf-invert {
    background-color: var(--pf-bg);
    color: var(--pf-text);
  }
  
  /* ==========================================================================
     #RESET
     ========================================================================== */
  
  /**
   * Deliberately small. Margins are zeroed and reinstated by the components
   * that need them, media elements are made block-level so they stop sitting on
   * the text baseline, and form controls inherit the page font rather than the
   * operating system's. Nothing else is normalised - modern engines agree about
   * far more than they used to.
   */
  
  *,
  *::before,
  *::after { box-sizing: border-box; }
  
  * { margin: 0; }
  
  html {
    -webkit-text-size-adjust: 100%;
    scroll-behavior: smooth;
    scroll-padding-top: 6rem;
  }
  
  body {
    background: var(--pf-bg);
    color: var(--pf-text);
    font-family: var(--pf-font-body);
    /* 17px at phone width, 19px on a desktop. The old 16px baseline is a
       default inherited from 1996 monitors, not a reading decision. */
    font-size: clamp(1.0625rem, 1.01rem + 0.28vw, 1.1875rem);
    line-height: var(--pf-leading);
    /* dvh after vh: on a phone, 100vh is the viewport with the address bar
       hidden, which is taller than what you can actually see. Older engines
       take the first, newer ones the second. */
    min-height: 100vh;
    min-height: 100dvh;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    text-rendering: optimizeLegibility;
    overflow-wrap: break-word;
    /* Inter's optical sizing and tabular figures in tables are handled per
       component; here we only turn on the kerning and ligatures that help at
       reading sizes. */
    font-kerning: normal;
    font-variant-ligatures: common-ligatures;
  }
  
  img,
  svg,
  video,
  canvas { display: block; max-width: 100%; height: auto; }
  
  /* Long URLs and unbroken identifiers are the usual cause of a page scrolling
     sideways on a phone; code is exempt because breaking it changes meaning. */
  pre, code, kbd, samp { overflow-wrap: normal; }
  
  input,
  button,
  textarea,
  select { font: inherit; color: inherit; }
  
  button { background: none; border: 0; }
  
  ul[class],
  ol[class] { list-style: none; padding: 0; }
  
  table { border-collapse: collapse; width: 100%; }
  
  :target { scroll-margin-top: 6rem; }
  
  /* ==========================================================================
     #TYPOGRAPHY
     ========================================================================== */
  
  /**
   * The scale is a minor third at the small end and widens toward the display
   * sizes, where clamp() takes over so headlines shrink on a phone without a
   * media query.
   *
   * p and li carry a max-width of --pf-measure (68ch). This is the one place
   * the framework imposes something on your content, and it is the difference
   * between a readable column and a line the eye cannot track back to. Opt out
   * per element with .pf-measure-none.
   */
  
  h1, h2, h3, h4, h5, h6 {
    font-family: var(--pf-font-display);
    font-weight: 700;
    line-height: var(--pf-leading-snug);
    letter-spacing: -0.015em;
    text-wrap: balance;
  }
  
  h1 { font-size: var(--pf-text-4xl); line-height: var(--pf-leading-tight); letter-spacing: -0.032em; }
  h2 { font-size: var(--pf-text-3xl); letter-spacing: -0.022em; }
  h3 { font-size: var(--pf-text-2xl); letter-spacing: -0.015em; }
  h4 { font-size: var(--pf-text-xl); letter-spacing: -0.01em; }
  h5 { font-size: var(--pf-text-lg); letter-spacing: -0.005em; }
  h6 { font-size: var(--pf-text-base); letter-spacing: 0; }
  
  /* A heading belongs to what follows it, so the space above is larger than the
     space below. Without this a heading floats between two blocks and the page
     reads as a list of fragments. */
  :where(h2, h3, h4) { margin-block-start: 0.4em; }
  
  p, li { max-width: var(--pf-measure); }
  p + p { margin-top: var(--pf-space-5); }
  
  a {
    color: var(--pf-accent);
    /* Underlines are on by default and skip descenders. A link identified only
       by colour fails for anyone who cannot separate that colour from the text
       around it. */
    text-decoration-color: rgba(143, 224, 106, 0.55);
    text-decoration-skip-ink: auto;
    text-underline-offset: 0.18em;
    text-decoration-thickness: 1.5px;
    transition: color var(--pf-speed) var(--pf-ease),
      text-decoration-color var(--pf-speed) var(--pf-ease);
  }
  a:hover { color: var(--pf-base-50); text-decoration-color: currentColor; }
  
  strong, b { font-weight: 600; color: var(--pf-base-50); }
  small { font-size: var(--pf-text-sm); }
  
  code, kbd, samp, pre { font-family: var(--pf-font-mono); font-size: 0.9em; }
  
  :not(pre) > code {
    background: var(--pf-hue-1-wash);
    border: 1px solid rgba(143, 224, 106, 0.22);
    border-radius: var(--pf-radius-sm);
    color: var(--pf-hue-1);
    padding: 0.12em 0.4em;
    white-space: nowrap;
  }
  
  kbd {
    background: var(--pf-surface-raised);
    border: 1px solid var(--pf-border);
    border-bottom-width: 2px;
    border-radius: var(--pf-radius-sm);
    font-size: var(--pf-text-xs);
    padding: 0.15em 0.45em;
  }
  
  hr {
    border: 0;
    border-top: 1px solid var(--pf-border);
    margin-block: var(--pf-space-6);
  }
  
  ::selection { background: var(--pf-hue-1); color: var(--pf-base-950); }
  
  /* Reader-facing lists (not class-styled component lists) */
  .pf-prose { line-height: var(--pf-leading-prose); }
  .pf-prose ul,
  .pf-prose ol { padding-inline-start: 1.4rem; display: grid; gap: var(--pf-space-3); }
  .pf-prose li::marker { color: var(--pf-accent); }
  .pf-prose > * + * { margin-top: var(--pf-space-5); }
  .pf-prose h2 { margin-top: var(--pf-space-8); }
  .pf-prose h3 { margin-top: var(--pf-space-7); }
  .pf-prose h4 { margin-top: var(--pf-space-6); }
  /* A heading immediately after another heading is a subtitle, not a new
     section, so it keeps the tighter spacing. */
  .pf-prose :where(h2, h3, h4) + :where(h2, h3, h4) { margin-top: var(--pf-space-4); }
  .pf-prose h2 + p,
  .pf-prose h3 + p { margin-top: var(--pf-space-4); }
  
  /* ==========================================================================
     #LAYOUT PRIMITIVES
     ========================================================================== */
  
  /**
   * Five primitives cover almost every page: a centred container, a vertical
   * stack, a horizontal cluster that wraps, a column grid, and a two-up split.
   * Compose them instead of writing one-off layout rules.
   *
   *   <div class="pf-container">
   *     <div class="pf-grid pf-grid--3"> ... </div>
   *   </div>
   */
  
  .pf-container {
    width: 100%;
    max-width: var(--pf-container);
    margin-inline: auto;
    /* max() with the safe-area inset keeps content clear of a notch or a
       rounded corner when a phone is held in landscape. It falls back to the
       gutter on every browser that does not know env(). */
    padding-inline: max(var(--pf-gutter), env(safe-area-inset-left), env(safe-area-inset-right));
  }
  .pf-container--narrow { max-width: 780px; }
  .pf-container--wide { max-width: 1360px; }
  
  .pf-stack { display: grid; gap: var(--pf-gap); }
  .pf-stack--sm { gap: var(--pf-space-2); }
  .pf-stack--lg { gap: var(--pf-space-6); }
  
  /* Reading measures. --prose is the long-form column; --wide suits reference
     pages where tables want more room than body copy does. */
  .pf-measure-prose { max-width: 72ch; }
  .pf-measure-wide { max-width: 74ch; }
  
  .pf-cluster {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--pf-space-3);
  }
  .pf-cluster--between { justify-content: space-between; }
  .pf-cluster--center { justify-content: center; }
  
  .pf-grid {
    display: grid;
    gap: var(--pf-gap);
    grid-template-columns: repeat(var(--pf-cols, 1), minmax(0, 1fr));
  }
  .pf-grid--2 { --pf-cols: 2; }
  .pf-grid--3 { --pf-cols: 3; }
  .pf-grid--4 { --pf-cols: 4; }
  /**
   * Auto-fitting grid
   *
   * <div class="pf-grid pf-grid--auto" style="--pf-min: 320px;"> ... </div>
   *
   * Columns are added as they fit rather than at fixed breakpoints. The
   * min(--pf-min, 100%) guard is what stops the track overflowing a viewport
   * narrower than --pf-min itself - without it, a 320px minimum breaks a 300px
   * phone.
   */
  .pf-grid--auto {
    grid-template-columns: repeat(auto-fit, minmax(min(var(--pf-min, 260px), 100%), 1fr));
  }
  /* Named minimums. Set --pf-min directly for anything in between. */
  .pf-grid--auto-sm { --pf-min: 200px; }
  .pf-grid--auto-md { --pf-min: 240px; }
  .pf-grid--auto-lg { --pf-min: 300px; }
  .pf-grid--auto-xl { --pf-min: 340px; }
  
  /**
   * Documentation shell
   *
   * <div class="pf-shell">
   *   <aside class="pf-sidenav"> ... </aside>
   *   <article> ... </article>
   * </div>
   *
   * Sidebar plus body. Collapses to one column below 900px, where a 220px rail
   * would leave the prose too narrow to read. --pf-rail overrides the width.
   */
  .pf-shell {
    display: grid;
    gap: var(--pf-gap-lg);
    grid-template-columns: var(--pf-rail, 220px) minmax(0, 1fr);
  }
  .pf-shell--narrow { --pf-rail: 180px; }
  
  /* Main column plus a secondary panel, for application views. */
  .pf-shell--aside {
    grid-template-columns: minmax(0, 2.2fr) minmax(280px, 1fr);
  }
  
  @media (max-width: 900px) {
    .pf-shell,
    .pf-shell--aside { grid-template-columns: minmax(0, 1fr); }
  }
  
  .pf-split {
    display: grid;
    gap: var(--pf-gap-lg);
    grid-template-columns: repeat(auto-fit, minmax(min(420px, 100%), 1fr));
    align-items: center;
  }
  
  /* A grid child with content wider than its track (a table, a long word, a
     <pre>) will otherwise push the track past 1fr and overflow the page. This
     one line prevents the single most common responsive bug in CSS grid. */
  .pf-grid > *,
  .pf-split > *,
  .pf-shell > * { min-width: 0; }
  
  @media (max-width: 1100px) {
    .pf-grid--4 { --pf-cols: 3; }
  }
  @media (max-width: 900px) {
    .pf-grid--3, .pf-grid--4 { --pf-cols: 2; }
  }
  @media (max-width: 620px) {
    .pf-grid--2, .pf-grid--3, .pf-grid--4 { --pf-cols: 1; }
  }
  
  /**
   * Facet
   *
   * <article class="pf-card pf-facet"> ... </article>
   *
   * One clipped corner, sized by --pf-cut. This is the framework's signature
   * shape; used on every card it becomes wallpaper, so reserve it for the
   * element you want read first. Set --pf-cut: 0 to remove the effect globally
   * without touching a component rule.
   *
   * Note that clip-path cuts everything, including outlines and box-shadows.
   * A focus ring on a clipped element is clipped too, which is why interactive
   * facet cards keep their ring on an inner element.
   */
  /* Brand mark inside a nav or footer lockup. Sized in rem so it tracks the
     surrounding text rather than a fixed pixel box. */
  .pf-logo {
    width: 2.1rem;
    height: 2.1rem;
    flex: none;
  }
  .pf-logo--sm { width: 1.6rem; height: 1.6rem; }
  
  /**
   * Standalone lockup
   *
   * <a class="pf-lockup" href="/"><svg class="pf-logo"></svg> PolyFish</a>
   *
   * The brand mark and wordmark outside a nav or footer - sign-in screens,
   * receipts, standalone panels.
   */
  .pf-lockup {
    display: inline-flex;
    align-items: center;
    gap: 0.6rem;
    color: var(--pf-text);
    font-family: var(--pf-font-display);
    font-size: 1.15rem;
    font-weight: 800;
    letter-spacing: -0.02em;
    text-decoration: none;
  }
  .pf-lockup:hover { color: var(--pf-text); }
  
  /* Brand tints, for the places the mark's own colours belong: a logo well, a
     brand pill, the footer lockup. Everything else uses the accent roles. */
  .pf-brand-well {
    background: color-mix(in srgb, var(--pf-brand) 16%, transparent);
    border: 1px solid color-mix(in srgb, var(--pf-brand) 40%, transparent);
  }
  .pf-fg-brand { color: var(--pf-brand-2); }
  .pf-brand-rule { border-inline-start: 2px solid var(--pf-brand); padding-inline-start: var(--pf-space-4); }
  
  /* Column span helpers for grid children. */
  .pf-span-full { grid-column: 1 / -1; }
  
  /**
   * Stretched link
   *
   * <article class="pf-card pf-relative">
   *   <h3><a class="pf-stretched-link" href="/post">Title</a></h3>
   *   <p>Body copy stays selectable.</p>
   * </article>
   *
   * Makes a whole card clickable from one anchor without wrapping the card in
   * it. The card needs a positioning context - add .pf-relative. Text inside
   * stays selectable, unlike a card-wide anchor, and there is still only one
   * tab stop.
   */
  .pf-stretched-link::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 1;
  }
  
  .pf-facet {
    clip-path: polygon(0 0, calc(100% - var(--pf-cut)) 0, 100% var(--pf-cut), 100% 100%, 0 100%);
  }
  .pf-facet--sw {
    clip-path: polygon(0 0, 100% 0, 100% 100%, var(--pf-cut) 100%, 0 calc(100% - var(--pf-cut)));
  }
  
  /* ==========================================================================
     #BUTTONS
     ========================================================================== */
  
  /**
   * Button
   *
   * <a class="pf-btn" href="/get">Get the files</a>
   * <button class="pf-btn pf-btn--secondary pf-btn--sm" type="button">Cancel</button>
   *
   * Variants:  --secondary (outline)  --ghost (bare)  --orange (secondary accent)
   * Sizes:     --sm  --lg  --block (full width)  --square (no pill radius)
   * States:    [disabled] and [aria-disabled="true"] both dim and block pointers
   *
   * The three local properties (--pf-btn-bg/-fg/-border) exist so a variant is
   * three declarations instead of a re-specification of the base. Padding is in
   * em, so a size variant only changes font-size and the shape follows.
   */
  
  .pf-btn {
    --pf-btn-bg: var(--pf-accent);
    --pf-btn-fg: var(--pf-base-950);
    --pf-btn-border: var(--pf-accent);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--pf-space-2);
    background: var(--pf-btn-bg);
    color: var(--pf-btn-fg);
    border: 1.5px solid var(--pf-btn-border);
    border-radius: var(--pf-radius-pill);
    font-family: var(--pf-font-body);
    font-size: var(--pf-text-sm);
    font-weight: 600;
    line-height: 1;
    padding: 0.82em 1.5em;
    text-decoration: none;
    cursor: pointer;
    transition: transform var(--pf-speed) var(--pf-ease),
      background-color var(--pf-speed) var(--pf-ease),
      border-color var(--pf-speed) var(--pf-ease),
      color var(--pf-speed) var(--pf-ease);
  }
  .pf-btn:hover { background: var(--pf-hue-1-deep); border-color: var(--pf-hue-1-deep); color: var(--pf-base-950); }
  .pf-btn:active { transform: translateY(1px); }
  
  .pf-btn--secondary {
    --pf-btn-bg: transparent;
    --pf-btn-fg: var(--pf-text);
    --pf-btn-border: var(--pf-border-strong);
  }
  .pf-btn--secondary:hover {
    background: var(--pf-hue-1-wash);
    border-color: var(--pf-accent);
    color: var(--pf-base-50);
  }
  
  .pf-btn--ghost {
    --pf-btn-bg: transparent;
    --pf-btn-fg: var(--pf-text-muted);
    --pf-btn-border: transparent;
  }
  .pf-btn--ghost:hover { background: var(--pf-surface); border-color: transparent; color: var(--pf-base-50); }
  
  .pf-btn--accent-2 {
    --pf-btn-bg: var(--pf-accent-2);
    --pf-btn-border: var(--pf-accent-2);
  }
  .pf-btn--accent-2:hover { background: var(--pf-hue-2-deep); border-color: var(--pf-hue-2-deep); }
  
  /**
   * Icon-only button
   *
   * <button class="pf-btn pf-btn--secondary pf-btn--icon" type="button"
   *         aria-label="Close">&times;</button>
   *
   * A square target with no text, which means aria-label is not optional - an
   * icon button without one announces as "button" and nothing else.
   */
  .pf-btn--icon {
    width: 2.75rem;
    height: 2.75rem;
    padding: 0;
    border-radius: 50%;
    font-size: var(--pf-text-lg);
    line-height: 1;
  }
  .pf-btn--icon.pf-btn--square { border-radius: var(--pf-radius); }
  .pf-btn--icon.pf-btn--sm { width: 2.25rem; height: 2.25rem; font-size: var(--pf-text-base); }
  
  /* A button that looks like a link but is still a button, for actions that do
     not navigate. Never use an <a> without an href for this. */
  .pf-btn--link {
    --pf-btn-bg: transparent;
    --pf-btn-fg: var(--pf-accent);
    --pf-btn-border: transparent;
    padding-inline: 0.35em;
    text-decoration: underline;
    text-underline-offset: 0.22em;
  }
  .pf-btn--link:hover { background: none; border-color: transparent; color: var(--pf-text); }
  
  /**
   * Busy state
   *
   * <button class="pf-btn" type="button" aria-busy="true" disabled>Saving</button>
   *
   * Driven by aria-busy so the visual and the announced state come from one
   * attribute. The spinner is a pseudo-element, so no markup changes when the
   * state flips.
   */
  .pf-btn[aria-busy="true"] { pointer-events: none; opacity: 0.75; }
  .pf-btn[aria-busy="true"]::before {
    content: "";
    width: 1em;
    height: 1em;
    border-radius: 50%;
    border: 2px solid currentColor;
    border-top-color: transparent;
    animation: pf-btn-spin 0.7s linear infinite;
  }
  @keyframes pf-btn-spin { to { transform: rotate(1turn); } }
  
  .pf-btn--sm { font-size: var(--pf-text-xs); padding: 0.65em 1.15em; }
  .pf-btn--lg { font-size: var(--pf-text-base); padding: 0.95em 2em; }
  .pf-btn--block { display: flex; width: 100%; }
  .pf-btn--square { border-radius: var(--pf-radius); }
  
  .pf-btn[disabled],
  .pf-btn[aria-disabled="true"] { opacity: 0.45; pointer-events: none; }
  
  /* ==========================================================================
     #PILLS, MARKERS, FOCUS
     ========================================================================== */
  
  /**
   * .pf-mark and .pf-dot are the small shapes used in place of tracked-out
   * capital labels: a clipped triangle and a circle, both sized in em so they
   * scale with whatever text they sit beside.
   *
   * The focus ring uses :focus-visible, so it appears for keyboard users and
   * stays out of the way of mouse clicks. It is declared with :where() to keep
   * specificity at zero, which means a component can override it without an
   * !important arms race.
   *
   * The reduced-motion block at the end is a blunt instrument by design: it
   * neutralises every transition and animation in every module at once, so a
   * new component cannot forget to honour the preference.
   */
  
  .pf-pill {
    display: inline-flex;
    align-items: center;
    gap: var(--pf-space-2);
    background: var(--pf-surface);
    border: 1px solid var(--pf-border);
    border-radius: var(--pf-radius-pill);
    color: var(--pf-text-muted);
    font-size: var(--pf-text-xs);
    font-weight: 500;
    padding: 0.4em 0.9em;
  }
  .pf-pill--accent { background: var(--pf-hue-1-wash); border-color: rgba(143, 224, 106, 0.35); color: var(--pf-hue-1); }
  .pf-pill--accent-2 { background: var(--pf-hue-2-wash); border-color: rgba(229, 177, 137, 0.35); color: var(--pf-hue-2); }
  
  /* Small triangle used instead of a tracked-out caps label. */
  .pf-mark {
    display: inline-block;
    width: 0.6em;
    height: 0.6em;
    background: var(--pf-accent);
    clip-path: polygon(50% 0, 100% 100%, 0 100%);
    flex: none;
  }
  .pf-mark--accent-2 { background: var(--pf-accent-2); }
  
  .pf-dot {
    width: 0.5em;
    height: 0.5em;
    border-radius: 50%;
    background: var(--pf-accent);
    flex: none;
  }
  
  :where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
    outline: 2px solid var(--pf-focus);
    outline-offset: 2px;
    border-radius: var(--pf-radius-sm);
  }
  
  .pf-skip {
    position: absolute;
    inset-inline-start: var(--pf-space-4);
    top: -4rem;
    z-index: 100;
    background: var(--pf-hue-1);
    color: var(--pf-base-950);
    border-radius: var(--pf-radius);
    padding: 0.7em 1.2em;
    font-weight: 600;
    text-decoration: none;
    transition: top var(--pf-speed) var(--pf-ease);
  }
  .pf-skip:focus { top: var(--pf-space-4); color: var(--pf-base-950); }
  
  /**
   * Scroll lock, set by the optional script while a drawer is open.
   *
   * overflow: hidden on <html> rather than <body>, and no position:fixed: the
   * fixed-body trick loses the scroll position and jumps the page to the top
   * on close, which on a long list is the most annoying possible outcome.
   */
  .pf-scroll-lock {
    overflow: hidden;
    /* Compensate for the scrollbar the lock removes, so the page does not
       shift sideways as the drawer opens on a desktop. */
    scrollbar-gutter: stable;
  }
  
  .pf-visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
  }
  
  /**
   * Touch targets.
   *
   * A 12px small button is about 28px tall, which is under every published
   * minimum for a finger. Rather than inflate every button on every device,
   * raise the floor only where the pointer is coarse - phones, tablets and
   * touch laptops - so mouse users keep the tighter rhythm.
   */
  /**
   * Touch behaviour.
   *
   * touch-action: manipulation removes the 300ms click delay that older mobile
   * browsers add while they wait to see if a tap is a double-tap to zoom. The
   * tap highlight is toned down rather than removed - removing it entirely
   * leaves a touch with no feedback at all until the next paint.
   */
  :where(a, button, label, summary, [role="button"]) {
    touch-action: manipulation;
    -webkit-tap-highlight-color: rgba(143, 224, 106, 0.18);
  }
  
  @media (pointer: coarse) {
    .pf-btn,
    .pf-nav__links a,
    .pf-footer__links a,
    .pf-sidenav__links a,
    .pf-breadcrumb a,
    .pf-social a,
    .pf-totop {
      min-height: 44px;
    }
    .pf-btn { display: inline-flex; align-items: center; }
    .pf-nav__links a,
    .pf-footer__links a,
    .pf-sidenav__links a { display: flex; align-items: center; }
    .pf-choice input { width: 1.25rem; height: 1.25rem; }
  
    /**
     * iOS Safari zooms the page when a field smaller than 16px receives focus,
     * and it does not zoom back out. Any font-size below 16px on a text input
     * is therefore a layout bug on the most common phone in the world. The
     * floor is applied only on touch pointers, so a desktop keeps the tighter
     * scale it was designed with.
     */
    .pf-input,
    .pf-select,
    .pf-textarea,
    .pf-input--sm,
    .pf-select--sm { font-size: 16px; }
  }
  
  /**
   * Short landscape viewports - a phone turned sideways, a 1024x600 laptop.
   * Vertical rhythm tuned for a tall screen eats the whole viewport here, so
   * the section and hero padding is capped rather than scaled.
   */
  @media (max-height: 640px) and (orientation: landscape) {
    :root {
      --pf-section-pad: clamp(2rem, 6vh, 3.5rem);
      --pf-gap-lg: clamp(1.5rem, 4vw, 2.5rem);
    }
  }
  
  /**
   * prefers-contrast: more. Users who ask the OS for stronger contrast get
   * solid borders instead of the hairlines, and muted text moves up to the
   * full-strength colour.
   */
  @media (prefers-contrast: more) {
    :root {
      --pf-border: rgba(232, 240, 246, 0.55);
      --pf-border-strong: var(--pf-accent);
      --pf-text-muted: #d3e2ec;
    }
    .pf-card, .pf-plan, .pf-alert, .pf-table-wrap, .pf-code { border-width: 2px; }
  }
  
  /**
   * Windows High Contrast / forced-colors.
   *
   * The OS replaces every colour with its own palette, which erases anything
   * expressed as a background rather than a border - buttons flatten into the
   * page and the facet corner disappears. Reinstating a border and honouring
   * the system keywords keeps the interface legible there.
   */
  @media (forced-colors: active) {
    .pf-btn,
    .pf-card,
    .pf-plan,
    .pf-alert,
    .pf-input,
    .pf-select,
    .pf-textarea,
    .pf-code { border: 1px solid ButtonBorder; }
  
    .pf-btn { forced-color-adjust: none; background: ButtonFace; color: ButtonText; }
    .pf-mark, .pf-dot { forced-color-adjust: none; background: CanvasText; }
    :where(a, button, input, select, textarea, summary):focus-visible { outline-color: Highlight; }
  }
  
  @media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }
    *, *::before, *::after {
      animation-duration: 0.01ms !important;
      animation-iteration-count: 1 !important;
      transition-duration: 0.01ms !important;
    }
  }
}
