/*! PolyFishCSS-Components.css | v1.0.0-rc.2-rc.1-rc.1-rc.1-rc.1-rc.1 | polyfish.org | MIT
 *
 * The interface pieces an application needs beyond a marketing page: badges,
 * button groups, pagination, progress, spinners, list groups, avatars,
 * switches, input groups, tabs, disclosure menus, dialogs, tooltips,
 * breadcrumb bars, skeletons and empty states.
 *
 * Depends on: PolyFishCSS-Core.css. Some components read Content's field
 *             styles (.pf-input) - each says so where it matters.
 * Load order: after Content, before Utilities.
 *
 * CONTENTS
 *   1.  Badges and chips
 *   2.  Button groups and split buttons
 *   3.  Pagination
 *   4.  Progress and meter
 *   5.  Spinner
 *   6.  List group
 *   7.  Avatar and avatar stack
 *   8.  Switch
 *   9.  Input group
 *   10. Tabs
 *   11. Disclosure menu
 *   12. Dialog
 *   13. Tooltip
 *   14. Ratio box
 *   15. Skeleton
 *   16. Empty state
 *   17. Divider
 *   18. Toolbar
 *
 * NO JAVASCRIPT, STILL
 *   Tabs are radio inputs. The disclosure menu is <details>. The dialog is a
 *   native <dialog> for the JavaScript case and a :target fallback for the
 *   scripted-free one. Anything that could not be done honestly without a
 *   script - a toast queue, a carousel, a combobox - is not here, because a
 *   half-working version of those is worse than none.
 */

@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.components`. 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.components {
  /* ==========================================================================
     #BADGES AND CHIPS
     ========================================================================== */
  
  /**
   * Badge
   *
   * <span class="pf-badge">New</span>
   * <span class="pf-badge pf-badge--accent">12</span>
   * <a class="pf-chip" href="/tags/css">css <span class="pf-chip__x">&times;</span></a>
   *
   * Variants: --accent  --secondary  --danger  --muted  --outline
   * Sizes are inherited: the badge is sized in em, so it matches whatever text
   * it sits inside rather than needing a size modifier.
   */
  .pf-badge {
    display: inline-flex;
    align-items: center;
    gap: 0.35em;
    background: var(--pf-surface-raised);
    border: 1px solid var(--pf-border);
    border-radius: var(--pf-radius-pill);
    color: var(--pf-text);
    font-size: 0.78em;
    font-weight: 600;
    line-height: 1;
    padding: 0.35em 0.7em;
    white-space: nowrap;
  }
  .pf-badge--accent { background: var(--pf-accent); border-color: var(--pf-accent); color: var(--pf-base-950); }
  .pf-badge--secondary { background: var(--pf-accent-2); border-color: var(--pf-accent-2); color: var(--pf-base-950); }
  .pf-badge--danger { background: #e08f8f; border-color: #e08f8f; color: var(--pf-base-950); }
  .pf-badge--muted { background: transparent; color: var(--pf-text-muted); }
  .pf-badge--outline { background: transparent; border-color: var(--pf-border-strong); color: var(--pf-accent); }
  
  /* Count badge riding on a button or nav item. */
  .pf-badge--count {
    min-width: 1.5em;
    justify-content: center;
    padding-inline: 0.45em;
  }
  
  /* Removable tag. The × is a child so the whole tag stays one hit target. */
  .pf-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.5em;
    background: var(--pf-surface);
    border: 1px solid var(--pf-border);
    border-radius: var(--pf-radius-sm);
    color: var(--pf-text-muted);
    font-size: var(--pf-text-sm);
    padding: 0.3em 0.6em;
    text-decoration: none;
  }
  .pf-chip:hover { border-color: var(--pf-border-strong); color: var(--pf-text); }
  .pf-chip__x { color: var(--pf-text-muted); font-size: 1.1em; line-height: 1; }
  
  /* ==========================================================================
     #BUTTON GROUPS
     ========================================================================== */
  
  /**
   * Button group
   *
   * <div class="pf-btn-group" role="group" aria-label="View">
   *   <button class="pf-btn pf-btn--secondary" type="button" aria-pressed="true">Day</button>
   *   <button class="pf-btn pf-btn--secondary" type="button">Week</button>
   * </div>
   *
   * The pill radius is flattened between members and restored at the ends, and
   * the shared border is collapsed with a negative margin so the seam is one
   * line rather than two. aria-pressed carries the selected state - it styles
   * itself, so the visual and the announced state cannot diverge.
   */
  .pf-btn-group {
    display: inline-flex;
    flex-wrap: wrap;
  }
  .pf-btn-group > .pf-btn { border-radius: 0; }
  .pf-btn-group > .pf-btn:not(:first-child) { margin-inline-start: -1.5px; }
  .pf-btn-group > .pf-btn:first-child {
    border-start-start-radius: var(--pf-radius-pill);
    border-end-start-radius: var(--pf-radius-pill);
  }
  .pf-btn-group > .pf-btn:last-child {
    border-start-end-radius: var(--pf-radius-pill);
    border-end-end-radius: var(--pf-radius-pill);
  }
  .pf-btn-group > .pf-btn:hover,
  .pf-btn-group > .pf-btn:focus-visible { position: relative; z-index: 1; }
  .pf-btn-group > .pf-btn[aria-pressed="true"] {
    background: var(--pf-accent);
    border-color: var(--pf-accent);
    color: var(--pf-base-950);
  }
  .pf-btn-group--square > .pf-btn:first-child,
  .pf-btn-group--square > .pf-btn:last-child {
    border-radius: var(--pf-radius);
  }
  .pf-btn-group--block { display: flex; }
  .pf-btn-group--block > .pf-btn { flex: 1; }
  
  /* ==========================================================================
     #PAGINATION
     ========================================================================== */
  
  /**
   * Pagination
   *
   * <nav class="pf-pagination" aria-label="Pages">
   *   <a class="pf-pagination__item" href="?p=1" rel="prev">Previous</a>
   *   <a class="pf-pagination__item" href="?p=1">1</a>
   *   <span class="pf-pagination__item" aria-current="page">2</span>
   *   <span class="pf-pagination__gap">…</span>
   * </nav>
   *
   * The current page is a <span>, not a link to itself, and carries
   * aria-current. Items are 2.5rem square so they clear the touch minimum.
   */
  .pf-pagination {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--pf-space-2);
  }
  .pf-pagination__item {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 2.5rem;
    min-height: 2.5rem;
    padding-inline: 0.75rem;
    background: var(--pf-surface);
    border: 1px solid var(--pf-border);
    border-radius: var(--pf-radius-sm);
    color: var(--pf-text-muted);
    font-size: var(--pf-text-sm);
    text-decoration: none;
    transition: border-color var(--pf-speed) var(--pf-ease), color var(--pf-speed) var(--pf-ease);
  }
  .pf-pagination__item:hover { border-color: var(--pf-border-strong); color: var(--pf-text); }
  .pf-pagination__item[aria-current] {
    background: var(--pf-accent);
    border-color: var(--pf-accent);
    color: var(--pf-base-950);
    font-weight: 600;
  }
  .pf-pagination__item[aria-disabled="true"] { opacity: 0.45; pointer-events: none; }
  .pf-pagination__gap { color: var(--pf-text-muted); padding-inline: 0.25rem; }
  
  /* ==========================================================================
     #PROGRESS AND METER
     ========================================================================== */
  
  /**
   * Progress
   *
   * <div class="pf-progress">
   *   <div class="pf-progress__bar" style="width: 62%" role="progressbar"
   *        aria-valuenow="62" aria-valuemin="0" aria-valuemax="100"></div>
   * </div>
   *
   * The width is the one place an inline style is unavoidable, because the
   * value is data. Everything else is here. Add --pf-progress-value if you
   * would rather set it as a custom property from your template.
   */
  .pf-progress {
    overflow: hidden;
    height: 0.6rem;
    background: var(--pf-surface-raised);
    border-radius: var(--pf-radius-pill);
  }
  .pf-progress__bar {
    height: 100%;
    width: var(--pf-progress-value, 0%);
    background: var(--pf-accent);
    border-radius: inherit;
    transition: width 400ms var(--pf-ease);
  }
  .pf-progress--lg { height: 1rem; }
  .pf-progress--secondary .pf-progress__bar { background: var(--pf-accent-2); }
  
  /* Segmented meter, for quotas and capacity. */
  .pf-meter {
    display: flex;
    gap: 3px;
    height: 0.6rem;
  }
  .pf-meter__seg {
    flex: 1;
    background: var(--pf-surface-raised);
    border-radius: 2px;
  }
  .pf-meter__seg--on { background: var(--pf-accent); }
  .pf-meter__seg--warn { background: var(--pf-accent-2); }
  
  /* ==========================================================================
     #SPINNER
     ========================================================================== */
  
  /**
   * Spinner
   *
   * <span class="pf-spinner" role="status" aria-label="Loading"></span>
   *
   * A rotating conic gradient rather than a border trick, so the tail fades
   * instead of stepping. Under prefers-reduced-motion it stops rotating and
   * becomes a static ring - Core's reduced-motion block handles that globally,
   * so a still ring must still read as "busy", which is why the aria-label is
   * part of the markup rather than optional.
   */
  .pf-spinner {
    display: inline-block;
    width: 1.25em;
    height: 1.25em;
    border-radius: 50%;
    background: conic-gradient(from 0turn, transparent 0deg, var(--pf-accent) 300deg);
    -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 3px), #000 0);
    mask: radial-gradient(farthest-side, transparent calc(100% - 3px), #000 0);
    animation: pf-spin 0.8s linear infinite;
    vertical-align: -0.2em;
  }
  .pf-spinner--lg { width: 2.5em; height: 2.5em; }
  @keyframes pf-spin { to { transform: rotate(1turn); } }
  
  /* ==========================================================================
     #LIST GROUP
     ========================================================================== */
  
  /**
   * List group
   *
   * Two valid shapes, and which one you need depends on the rows:
   *
   *   <ul class="pf-list">                    static rows
   *     <li class="pf-list__item">Label</li>
   *   </ul>
   *
   *   <div class="pf-list">                   rows that are links
   *     <div class="pf-list__item">Label</div>
   *     <a class="pf-list__item pf-list__item--link" href="/x">Clickable row</a>
   *   </div>
   *
   * An <a> is not valid as a direct child of <ul>, and wrapping each anchor in
   * an <li> breaks the adjacent-sibling selector that collapses the shared
   * borders. When any row is a link, use the <div> form.
   *
   * Rows share one border by collapsing them, so a list of ten does not read as
   * ten separate boxes. Use it for settings, file lists and result rows - the
   * places a table would be overkill.
   */
  .pf-list {
    display: flex;
    flex-direction: column;
    border: 1px solid var(--pf-border);
    border-radius: var(--pf-radius);
    overflow: hidden;
    margin: 0;
  }
  .pf-list__item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--pf-space-4);
    background: var(--pf-surface);
    color: var(--pf-text);
    font-size: var(--pf-text-sm);
    padding: var(--pf-space-4) var(--pf-space-5);
    text-decoration: none;
    max-width: none;
  }
  .pf-list__item + .pf-list__item { border-top: 1px solid var(--pf-border); }
  .pf-list__item--link:hover { background: var(--pf-surface-raised); }
  .pf-list__item[aria-current] { box-shadow: inset 3px 0 0 var(--pf-accent); }
  .pf-list__title { font-weight: 600; }
  .pf-list__meta { color: var(--pf-text-muted); font-size: var(--pf-text-xs); }
  .pf-list--flush { border: 0; border-radius: 0; }
  .pf-list--flush .pf-list__item { background: none; padding-inline: 0; }
  
  /* ==========================================================================
     #AVATAR
     ========================================================================== */
  
  /**
   * Avatar
   *
   * <span class="pf-avatar" aria-hidden="true">JS</span>
   * <img class="pf-avatar" src="..." alt="Jo Silva">
   *
   * Initials are the default because an avatar without an image is the common
   * case in an internal tool. When it is decorative next to a name, mark it
   * aria-hidden so the name is not announced twice.
   */
  .pf-avatar {
    display: inline-grid;
    place-items: center;
    width: 2.25rem;
    height: 2.25rem;
    flex: none;
    background: var(--pf-surface-raised);
    border: 1px solid var(--pf-border);
    border-radius: 50%;
    color: var(--pf-text);
    font-size: var(--pf-text-xs);
    font-weight: 600;
    letter-spacing: 0.02em;
    object-fit: cover;
    overflow: hidden;
  }
  .pf-avatar--sm { width: 1.6rem; height: 1.6rem; font-size: 0.6rem; }
  .pf-avatar--lg { width: 3.25rem; height: 3.25rem; font-size: var(--pf-text-sm); }
  .pf-avatar--accent { background: var(--pf-accent); border-color: var(--pf-accent); color: var(--pf-base-950); }
  
  /* Overlapping stack. The ring is the page background, so members read as
     separate discs on any surface. */
  .pf-avatars { display: flex; }
  .pf-avatars > .pf-avatar + .pf-avatar { margin-inline-start: -0.6rem; }
  .pf-avatars > .pf-avatar { box-shadow: 0 0 0 2px var(--pf-bg); }
  
  /* ==========================================================================
     #SWITCH
     ========================================================================== */
  
  /**
   * Switch
   *
   * <label class="pf-switch">
   *   <input type="checkbox">
   *   <span class="pf-switch__track"></span>
   *   Weekly digest
   * </label>
   *
   * A real checkbox, visually hidden but focusable, with the track drawn from
   * its :checked state. Keyboard, form submission and autofill all behave
   * because the control was never replaced.
   */
  .pf-switch {
    display: inline-flex;
    align-items: center;
    gap: var(--pf-space-3);
    cursor: pointer;
    font-size: var(--pf-text-sm);
  }
  .pf-switch input {
    position: absolute;
    opacity: 0;
    width: 2.6rem;
    height: 1.5rem;
    margin: 0;
    cursor: pointer;
  }
  .pf-switch__track {
    position: relative;
    width: 2.6rem;
    height: 1.5rem;
    flex: none;
    background: var(--pf-surface-raised);
    border: 1px solid var(--pf-border);
    border-radius: var(--pf-radius-pill);
    transition: background-color var(--pf-speed) var(--pf-ease), border-color var(--pf-speed) var(--pf-ease);
  }
  .pf-switch__track::after {
    content: "";
    position: absolute;
    top: 50%;
    inset-inline-start: 3px;
    width: 1.05rem;
    height: 1.05rem;
    background: var(--pf-text-muted);
    border-radius: 50%;
    transform: translateY(-50%);
    transition: transform var(--pf-speed) var(--pf-ease), background-color var(--pf-speed) var(--pf-ease);
  }
  .pf-switch input:checked + .pf-switch__track {
    background: var(--pf-accent);
    border-color: var(--pf-accent);
  }
  .pf-switch input:checked + .pf-switch__track::after {
    background: var(--pf-base-950);
    transform: translate(1.05rem, -50%);
  }
  .pf-switch input:focus-visible + .pf-switch__track {
    outline: 2px solid var(--pf-focus);
    outline-offset: 2px;
  }
  .pf-switch input:disabled + .pf-switch__track { opacity: 0.45; }
  
  /* ==========================================================================
     #INPUT GROUP
     ========================================================================== */
  
  /**
   * Input group
   *
   * <div class="pf-input-group">
   *   <span class="pf-input-group__addon">https://</span>
   *   <input class="pf-input" type="text">
   *   <button class="pf-btn pf-btn--square" type="submit">Go</button>
   * </div>
   *
   * Needs Content for .pf-input. The focus ring is lifted to the whole group so
   * a focused field does not sit half-outlined against its addon.
   */
  .pf-input-group {
    display: flex;
    align-items: stretch;
    width: 100%;
  }
  .pf-input-group > * { border-radius: 0; }
  .pf-input-group > :first-child {
    border-start-start-radius: var(--pf-radius);
    border-end-start-radius: var(--pf-radius);
  }
  .pf-input-group > :last-child {
    border-start-end-radius: var(--pf-radius);
    border-end-end-radius: var(--pf-radius);
  }
  .pf-input-group > * + * { margin-inline-start: -1px; }
  .pf-input-group .pf-input { flex: 1; min-width: 0; }
  .pf-input-group .pf-input:focus { position: relative; z-index: 1; }
  .pf-input-group__addon {
    display: inline-flex;
    align-items: center;
    background: var(--pf-surface-raised);
    border: 1px solid var(--pf-border);
    color: var(--pf-text-muted);
    font-size: var(--pf-text-sm);
    padding-inline: var(--pf-space-4);
    white-space: nowrap;
  }
  
  /* ==========================================================================
     #TABS
     ========================================================================== */
  
  /**
   * Tabs, without JavaScript
   *
   * <div class="pf-tabs">
   *   <input class="pf-tabs__radio" type="radio" name="t" id="t1" checked>
   *   <label class="pf-tabs__label" for="t1">Overview</label>
   *   <input class="pf-tabs__radio" type="radio" name="t" id="t2">
   *   <label class="pf-tabs__label" for="t2">Details</label>
   *
   *   <div class="pf-tabs__panel"> ... first panel ... </div>
   *   <div class="pf-tabs__panel"> ... second panel ... </div>
   * </div>
   *
   * Radios in one group, labels styled as the strip, panels revealed by
   * :checked and :nth-of-type. Arrow keys move between radios natively, which
   * is the behaviour a scripted tablist has to reimplement.
   *
   * LIMIT: with no script there is no role="tab" wiring, so a screen reader
   * announces this as a radio group rather than a tablist. That is honest and
   * usable. If you need the full ARIA tab pattern you need the script.
   */
  .pf-tabs {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(0, max-content));
    border-bottom: 1px solid var(--pf-border);
    column-gap: var(--pf-space-2);
  }
  
  /**
   * Five tabs with real labels do not fit on a 320px screen. The strip scrolls
   * horizontally instead of wrapping, because a wrapped tab strip stops reading
   * as one control, and the panel keeps its full width underneath.
   */
  @media (max-width: 620px) {
    .pf-tabs {
      grid-auto-flow: column;
      grid-template-columns: none;
      justify-content: start;
      overflow-x: auto;
      overscroll-behavior-x: contain;
      scrollbar-width: thin;
    }
    .pf-tabs__panel { grid-column: 1 / -1; }
  }
  .pf-tabs__radio {
    position: absolute;
    opacity: 0;
    pointer-events: none;
  }
  .pf-tabs__label {
    grid-row: 1;
    padding: var(--pf-space-3) var(--pf-space-4);
    border-bottom: 2px solid transparent;
    color: var(--pf-text-muted);
    cursor: pointer;
    font-size: var(--pf-text-sm);
    font-weight: 500;
    white-space: nowrap;
  }
  .pf-tabs__label:hover { color: var(--pf-text); }
  .pf-tabs__panel {
    grid-row: 2;
    grid-column: 1 / -1;
    display: none;
    padding-top: var(--pf-space-5);
  }
  .pf-tabs__radio:checked + .pf-tabs__label {
    border-bottom-color: var(--pf-accent);
    color: var(--pf-text);
  }
  .pf-tabs__radio:focus-visible + .pf-tabs__label {
    outline: 2px solid var(--pf-focus);
    outline-offset: -2px;
  }
  .pf-tabs__radio:nth-of-type(1):checked ~ .pf-tabs__panel:nth-of-type(1),
  .pf-tabs__radio:nth-of-type(2):checked ~ .pf-tabs__panel:nth-of-type(2),
  .pf-tabs__radio:nth-of-type(3):checked ~ .pf-tabs__panel:nth-of-type(3),
  .pf-tabs__radio:nth-of-type(4):checked ~ .pf-tabs__panel:nth-of-type(4),
  .pf-tabs__radio:nth-of-type(5):checked ~ .pf-tabs__panel:nth-of-type(5) { display: block; }
  
  /* ==========================================================================
     #DISCLOSURE MENU
     ========================================================================== */
  
  /**
   * Disclosure menu
   *
   * <details class="pf-menu">
   *   <summary class="pf-menu__trigger">Actions</summary>
   *   <div class="pf-menu__list">
   *     <a class="pf-menu__item" href="/edit">Edit</a>
   *     <hr class="pf-menu__sep">
   *     <button class="pf-menu__item pf-menu__item--danger" type="button">Delete</button>
   *   </div>
   * </details>
   *
   * A dropdown built on <details>, so it opens on click and on Enter, closes on
   * Escape in current browsers, and needs no script.
   *
   * LIMIT: it does not close when you click elsewhere on the page. That genuinely
   * requires a listener. If that matters, wire one up - the CSS does not change.
   */
  .pf-menu { position: relative; display: inline-block; }
  .pf-menu__trigger {
    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);
    cursor: pointer;
    font-size: var(--pf-text-sm);
    font-weight: 500;
    list-style: none;
    padding: 0.7em 1.2em;
  }
  .pf-menu__trigger::-webkit-details-marker { display: none; }
  .pf-menu__trigger::after {
    content: "";
    width: 0.55em;
    height: 0.55em;
    background: var(--pf-accent);
    clip-path: polygon(50% 100%, 0 0, 100% 0);
  }
  .pf-menu[open] .pf-menu__trigger { border-color: var(--pf-border-strong); }
  .pf-menu__list {
    position: absolute;
    z-index: 40;
    top: calc(100% + 0.4rem);
    inset-inline-start: 0;
    min-width: 12rem;
    /* Capped against the viewport: a 12rem minimum anchored near the right
       edge of a 320px screen would otherwise sit half off it. */
    max-width: min(18rem, calc(100vw - 2rem));
    display: grid;
    background: var(--pf-bg-deep);
    border: 1px solid var(--pf-border);
    border-radius: var(--pf-radius);
    box-shadow: var(--pf-lift);
    padding: var(--pf-space-2);
  }
  .pf-menu--end .pf-menu__list { inset-inline-start: auto; inset-inline-end: 0; }
  .pf-menu__item {
    display: flex;
    align-items: center;
    gap: var(--pf-space-3);
    background: none;
    border: 0;
    border-radius: var(--pf-radius-sm);
    color: var(--pf-text);
    cursor: pointer;
    font-size: var(--pf-text-sm);
    padding: var(--pf-space-2) var(--pf-space-3);
    text-align: start;
    text-decoration: none;
    width: 100%;
  }
  .pf-menu__item:hover { background: var(--pf-surface-raised); color: var(--pf-text); }
  .pf-menu__item--danger { color: #e08f8f; }
  .pf-menu__sep { border: 0; border-top: 1px solid var(--pf-border); margin: var(--pf-space-2) 0; }
  
  /* ==========================================================================
     #DIALOG
     ========================================================================== */
  
  /**
   * Dialog
   *
   * Native, when you have a script to call showModal():
   *   <dialog class="pf-dialog"> ... </dialog>
   *
   * Or CSS-only, addressed by fragment:
   *   <a class="pf-btn" href="#confirm">Delete</a>
   *   <div class="pf-dialog-target" id="confirm">
   *     <div class="pf-dialog__panel">
   *       <a class="pf-dialog__close" href="#" aria-label="Close">&times;</a>
   *       ...
   *     </div>
   *   </div>
   *
   * The native version gets focus trapping and Escape for free and should be
   * preferred whenever a script exists. The :target version is a genuine
   * fallback, not a replacement: it does not trap focus, so use it for simple
   * confirmations rather than complex forms.
   */
  .pf-dialog {
    max-width: min(32rem, calc(100vw - 2rem));
    background: var(--pf-bg-deep);
    border: 1px solid var(--pf-border-strong);
    border-radius: var(--pf-radius-lg);
    color: var(--pf-text);
    padding: var(--pf-space-6);
  }
  .pf-dialog::backdrop { background: rgba(0, 4, 18, 0.72); }
  
  .pf-dialog-target {
    position: fixed;
    inset: 0;
    z-index: 60;
    display: none;
    place-items: center;
    padding: var(--pf-space-4);
    background: rgba(0, 4, 18, 0.72);
  }
  .pf-dialog-target:target { display: grid; }
  .pf-dialog__panel {
    position: relative;
    width: min(32rem, 100%);
    background: var(--pf-bg-deep);
    border: 1px solid var(--pf-border-strong);
    border-radius: var(--pf-radius-lg);
    padding: var(--pf-space-6);
  }
  .pf-dialog__close {
    position: absolute;
    top: var(--pf-space-3);
    inset-inline-end: var(--pf-space-4);
    color: var(--pf-text-muted);
    font-size: 1.4rem;
    line-height: 1;
    text-decoration: none;
  }
  .pf-dialog__close:hover { color: var(--pf-text); }
  .pf-dialog__title { font-family: var(--pf-font-display); font-size: var(--pf-text-xl); }
  .pf-dialog__foot {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: var(--pf-space-3);
    margin-top: var(--pf-space-5);
  }
  
  /* ==========================================================================
     #TOOLTIP
     ========================================================================== */
  
  /**
   * Tooltip
   *
   * <button class="pf-btn pf-tooltip" data-pf-tip="Copies to clipboard">Copy</button>
   *
   * Shown on hover AND on keyboard focus - hover-only tooltips are invisible to
   * anyone tabbing through. The text comes from a data attribute so it is not
   * announced twice; when the tip carries information the control does not, put
   * it in aria-describedby content instead of here.
   */
  .pf-tooltip { position: relative; }
  .pf-tooltip::after {
    content: attr(data-pf-tip);
    position: absolute;
    bottom: calc(100% + 0.5rem);
    inset-inline-start: 50%;
    z-index: 50;
    width: max-content;
    max-width: 16rem;
    background: var(--pf-bg-deep);
    border: 1px solid var(--pf-border);
    border-radius: var(--pf-radius-sm);
    color: var(--pf-text);
    font-size: var(--pf-text-xs);
    font-weight: 400;
    line-height: 1.4;
    padding: 0.4rem 0.6rem;
    opacity: 0;
    pointer-events: none;
    transform: translate(-50%, 0.25rem);
    transition: opacity var(--pf-speed) var(--pf-ease), transform var(--pf-speed) var(--pf-ease);
  }
  .pf-tooltip:hover::after,
  .pf-tooltip:focus-visible::after { opacity: 1; transform: translate(-50%, 0); }
  .pf-tooltip--end::after { inset-inline-start: auto; inset-inline-end: 0; transform: translate(0, 0.25rem); }
  .pf-tooltip--end:hover::after,
  .pf-tooltip--end:focus-visible::after { transform: translate(0, 0); }
  
  /* ==========================================================================
     #RATIO BOX
     ========================================================================== */
  
  /**
   * Ratio
   *
   * <div class="pf-ratio pf-ratio--16x9">
   *   <iframe src="..." title="..."></iframe>
   * </div>
   *
   * Holds space for an embed before it loads, which is most of the layout shift
   * on a page with video.
   */
  .pf-ratio { position: relative; width: 100%; aspect-ratio: var(--pf-ratio, 16 / 9); }
  .pf-ratio > * { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
  .pf-ratio--1x1 { --pf-ratio: 1 / 1; }
  .pf-ratio--4x3 { --pf-ratio: 4 / 3; }
  .pf-ratio--16x9 { --pf-ratio: 16 / 9; }
  .pf-ratio--21x9 { --pf-ratio: 21 / 9; }
  
  /* ==========================================================================
     #SKELETON
     ========================================================================== */
  
  /**
   * Skeleton
   *
   * <div class="pf-skeleton pf-skeleton--title"></div>
   * <div class="pf-skeleton pf-skeleton--text"></div>
   *
   * Placeholder blocks at the shape of the content that is coming. Mark the
   * region aria-busy="true" so it is not read as empty content.
   */
  .pf-skeleton {
    background: linear-gradient(90deg, var(--pf-surface) 25%, var(--pf-surface-raised) 37%, var(--pf-surface) 63%);
    background-size: 400% 100%;
    border-radius: var(--pf-radius-sm);
    height: 0.9rem;
    animation: pf-skeleton 1.4s ease infinite;
  }
  .pf-skeleton--title { height: 1.6rem; width: 60%; }
  .pf-skeleton--text { width: 100%; margin-top: var(--pf-space-3); }
  .pf-skeleton--text:nth-of-type(3n) { width: 82%; }
  .pf-skeleton--block { height: 8rem; border-radius: var(--pf-radius); }
  .pf-skeleton--circle { height: 2.25rem; width: 2.25rem; border-radius: 50%; }
  @keyframes pf-skeleton { from { background-position: 100% 50%; } to { background-position: 0 50%; } }
  
  /* ==========================================================================
     #EMPTY STATE
     ========================================================================== */
  
  /**
   * Empty state
   *
   * <div class="pf-empty">
   *   <span class="pf-empty__mark"></span>
   *   <h3 class="pf-empty__title">No checks yet</h3>
   *   <p class="pf-empty__body">Point us at a URL and we will start watching it.</p>
   *   <a class="pf-btn" href="/new">Add a check</a>
   * </div>
   *
   * Say what is missing and give the action that fixes it. An empty table with
   * no explanation is the most common unfinished screen in an internal tool.
   */
  .pf-empty {
    display: grid;
    justify-items: center;
    gap: var(--pf-space-3);
    border: 1px dashed var(--pf-border);
    border-radius: var(--pf-radius-lg);
    padding: var(--pf-space-8) var(--pf-space-5);
    text-align: center;
  }
  .pf-empty__mark {
    width: 2.5rem;
    height: 2.5rem;
    background: var(--pf-accent);
    opacity: 0.5;
    clip-path: polygon(50% 0, 100% 100%, 0 100%);
  }
  .pf-empty__title { font-family: var(--pf-font-display); font-size: var(--pf-text-xl); }
  .pf-empty__body { color: var(--pf-text-muted); max-width: 44ch; }
  
  /* ==========================================================================
     #DIVIDER
     ========================================================================== */
  
  /**
   * Divider with an optional label:
   *   <div class="pf-divider"><span>or</span></div>
   */
  .pf-divider {
    display: flex;
    align-items: center;
    gap: var(--pf-space-4);
    color: var(--pf-text-muted);
    font-size: var(--pf-text-xs);
  }
  .pf-divider::before,
  .pf-divider::after {
    content: "";
    flex: 1;
    height: 1px;
    background: var(--pf-border);
  }
  
  /* ==========================================================================
     #DRAWER
     ========================================================================== */
  
  /**
   * Drawer (offcanvas)
   *
   * <a class="pf-btn" href="#filters">Filters</a>
   *
   * <div class="pf-drawer" id="filters" role="dialog" aria-modal="true"
   *      aria-labelledby="filters-title">
   *   <a class="pf-drawer__scrim" href="#" aria-label="Close"></a>
   *   <div class="pf-drawer__panel">
   *     <div class="pf-drawer__head">
   *       <h2 class="pf-drawer__title" id="filters-title">Filters</h2>
   *       <a class="pf-btn pf-btn--ghost pf-btn--icon pf-btn--sm" href="#"
   *          aria-label="Close">&times;</a>
   *     </div>
   *     <div class="pf-drawer__body"> ... </div>
   *   </div>
   * </div>
   *
   * Variants: --end (slides from the right)  --bottom (a phone sheet)
   *
   * Opens on :target with no script. Add data-pf-drawer to have the optional
   * script close it on Escape and return focus to the trigger.
   *
   * The panel is a flex column with a scrolling body, so a long filter list
   * scrolls inside the drawer instead of the page behind it. Both the panel and
   * the body carry safe-area padding: a bottom sheet on a gesture-navigation
   * phone otherwise ends underneath the home indicator.
   */
  .pf-drawer {
    position: fixed;
    inset: 0;
    z-index: 70;
    display: none;
  }
  .pf-drawer:target { display: block; }
  
  .pf-drawer__scrim {
    position: absolute;
    inset: 0;
    background: rgba(0, 4, 18, 0.6);
    display: block;
  }
  
  .pf-drawer__panel {
    position: absolute;
    inset-block: 0;
    inset-inline-start: 0;
    display: flex;
    flex-direction: column;
    width: min(22rem, 88vw);
    background: var(--pf-bg-deep);
    border-inline-end: 1px solid var(--pf-border);
    box-shadow: var(--pf-lift);
    padding-bottom: env(safe-area-inset-bottom);
  }
  .pf-drawer--end .pf-drawer__panel {
    inset-inline: auto 0;
    border-inline: 1px solid var(--pf-border) none;
  }
  .pf-drawer--bottom .pf-drawer__panel {
    inset: auto 0 0;
    width: auto;
    max-height: 82dvh;
    border: 1px solid var(--pf-border);
    border-radius: var(--pf-radius-lg) var(--pf-radius-lg) 0 0;
  }
  
  .pf-drawer__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--pf-space-4);
    border-bottom: 1px solid var(--pf-border);
    padding: var(--pf-space-4) var(--pf-space-5);
  }
  .pf-drawer__title { font-family: var(--pf-font-display); font-size: var(--pf-text-lg); }
  .pf-drawer__body {
    flex: 1;
    overflow-y: auto;
    overscroll-behavior: contain;
    padding: var(--pf-space-5);
  }
  .pf-drawer__foot {
    border-top: 1px solid var(--pf-border);
    display: flex;
    gap: var(--pf-space-3);
    padding: var(--pf-space-4) var(--pf-space-5);
    padding-bottom: max(var(--pf-space-4), env(safe-area-inset-bottom));
  }
  .pf-drawer__foot > .pf-btn { flex: 1; }
  
  /* A grab handle reads as "this can be dismissed" on a bottom sheet. */
  .pf-drawer--bottom .pf-drawer__head::before {
    content: "";
    position: absolute;
    top: 0.5rem;
    inset-inline-start: 50%;
    width: 2.5rem;
    height: 0.25rem;
    border-radius: var(--pf-radius-pill);
    background: var(--pf-border-strong);
    transform: translateX(-50%);
  }
  .pf-drawer--bottom .pf-drawer__head { position: relative; padding-top: var(--pf-space-6); }
  
  /* ==========================================================================
     #BOTTOM BAR
     ========================================================================== */
  
  /**
   * Bottom bar
   *
   * <nav class="pf-bottombar" aria-label="Primary">
   *   <a class="pf-bottombar__item" href="/" aria-current="page">
   *     <span class="pf-bottombar__mark" aria-hidden="true"></span> Home
   *   </a>
   * </nav>
   *
   * A phone-only navigation bar pinned to the bottom, where a thumb actually
   * reaches. Hidden from 860px up, where the top navigation takes over.
   *
   * Two details that are not optional: the safe-area padding, or the last row
   * of items sits under the home indicator, and the matching padding on <body>
   * (.pf-has-bottombar), or the bar covers the end of the page with no way to
   * scroll past it.
   */
  .pf-bottombar {
    position: fixed;
    inset: auto 0 0;
    z-index: 45;
    display: flex;
    justify-content: space-around;
    gap: var(--pf-space-2);
    background: color-mix(in srgb, var(--pf-bg-deep) 92%, transparent);
    -webkit-backdrop-filter: blur(12px);
    backdrop-filter: blur(12px);
    border-top: 1px solid var(--pf-border);
    padding: var(--pf-space-2) var(--pf-space-3);
    padding-bottom: max(var(--pf-space-2), env(safe-area-inset-bottom));
  }
  .pf-bottombar__item {
    display: grid;
    justify-items: center;
    gap: 0.2rem;
    flex: 1;
    min-height: 44px;
    align-content: center;
    border-radius: var(--pf-radius-sm);
    color: var(--pf-text-muted);
    font-size: var(--pf-text-xs);
    text-decoration: none;
  }
  .pf-bottombar__item[aria-current] { color: var(--pf-accent); }
  .pf-bottombar__mark {
    width: 1.1rem;
    height: 1.1rem;
    background: currentColor;
    clip-path: polygon(50% 0, 100% 100%, 0 100%);
  }
  .pf-has-bottombar { padding-bottom: calc(4.5rem + env(safe-area-inset-bottom)); }
  
  @media (min-width: 860px) {
    .pf-bottombar { display: none; }
    .pf-has-bottombar { padding-bottom: 0; }
  }
  
  /* ==========================================================================
     #TOAST
     ========================================================================== */
  
  /**
   * Toast
   *
   * The region is markup you add once; the messages come from the script:
   *
   *   <div class="pf-toasts" id="pf-toasts" role="status" aria-live="polite"></div>
   *   PolyFish.toast("Check created");
   *
   * This is the one component that genuinely requires JavaScript, and it is
   * marked as such: there is no CSS-only fallback, because a toast with no
   * script is an empty box.
   *
   * role="status" with aria-live="polite" queues the message behind whatever
   * the screen reader is currently saying. An assertive live region interrupts
   * mid-sentence, which for a confirmation message is rude rather than urgent.
   *
   * Positioned above the bottom bar and clear of the safe area.
   */
  .pf-toasts {
    position: fixed;
    inset: auto 0 0;
    z-index: 80;
    display: grid;
    justify-items: center;
    gap: var(--pf-space-2);
    padding: var(--pf-space-4);
    padding-bottom: max(var(--pf-space-4), env(safe-area-inset-bottom));
    pointer-events: none;
  }
  .pf-has-bottombar .pf-toasts { padding-bottom: calc(4.5rem + env(safe-area-inset-bottom)); }
  
  .pf-toast {
    display: flex;
    align-items: center;
    gap: var(--pf-space-3);
    width: min(26rem, 100%);
    background: var(--pf-bg-deep);
    border: 1px solid var(--pf-border-strong);
    border-radius: var(--pf-radius);
    box-shadow: var(--pf-lift);
    color: var(--pf-text);
    font-size: var(--pf-text-sm);
    padding: var(--pf-space-3) var(--pf-space-4);
    pointer-events: auto;
    animation: pf-toast-in 220ms var(--pf-ease) both;
  }
  .pf-toast--accent { border-color: var(--pf-accent); }
  .pf-toast--danger { border-color: #e08f8f; }
  .pf-toast__close { margin-inline-start: auto; }
  
  @keyframes pf-toast-in {
    from { opacity: 0; transform: translateY(0.75rem); }
    to { opacity: 1; transform: none; }
  }
  
  @media (min-width: 620px) {
    .pf-toasts { inset: auto 0 0 auto; justify-items: end; }
  }
  
  /* ==========================================================================
     #TOOLBAR
     ========================================================================== */
  
  /**
   * Toolbar
   *
   * <div class="pf-toolbar">
   *   <div class="pf-toolbar__group"> ... </div>
   *   <div class="pf-toolbar__group pf-toolbar__group--end"> ... </div>
   * </div>
   *
   * The row of controls above a table or list. Wraps rather than scrolls, so a
   * narrow screen stacks the groups instead of hiding half of them.
   */
  .pf-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--pf-space-4);
    padding-block: var(--pf-space-3);
  }
  .pf-toolbar__group { display: flex; flex-wrap: wrap; align-items: center; gap: var(--pf-space-3); }
  .pf-toolbar__group--end { margin-inline-start: auto; }
  .pf-toolbar--bordered {
    border: 1px solid var(--pf-border);
    border-radius: var(--pf-radius);
    padding: var(--pf-space-3) var(--pf-space-4);
  }
}
