/*
 * Everything here is driven by custom properties, so a project restyles the
 * bar by setting them rather than by overriding the rules:
 *
 *   :root {
 *       --pb-progress-color: #e11d48;
 *       --pb-progress-height: 4px;
 *       --pb-progress-shadow: 0 0 12px var(--pb-progress-color);
 *   }
 *
 * Replacing it altogether is a matter of styling .pb-progress from scratch:
 * the element carries that class from the moment a navigation starts, gains
 * .pb-progress-running on the frame after, and .pb-progress-done once the page
 * has arrived. It is removed when its transition ends.
 */
:root {
    --pb-progress-color: #3498db;
    --pb-progress-height: 3px;
    --pb-progress-shadow: 0 0 8px rgba(52, 152, 219, 0.6);
    --pb-progress-z-index: 9999;

    /* How long the bar takes to creep towards the end while waiting */
    --pb-progress-duration: 8s;

    /* And to run the rest of the way once the page is there */
    --pb-progress-settle: 200ms;
}

/* Navigation progress bar */
.pb-progress {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: var(--pb-progress-height);
    z-index: var(--pb-progress-z-index);
    background: var(--pb-progress-color);
    box-shadow: var(--pb-progress-shadow);
    transform: scaleX(0);
    transform-origin: 0 50%;
    pointer-events: none;
}

/*
 * Creeping rather than counting: how long a page takes is not known when the
 * bar starts, so it eases towards the end without ever arriving there.
 */
.pb-progress-running {
    transform: scaleX(0.9);
    transition: transform var(--pb-progress-duration) cubic-bezier(0.1, 0.9, 0.2, 1);
}

.pb-progress-done {
    transform: scaleX(1);
    opacity: 0;
    transition:
        transform var(--pb-progress-settle) ease-out,
        opacity var(--pb-progress-settle) ease-out var(--pb-progress-settle);
}

@media (prefers-reduced-motion: reduce) {
    .pb-progress,
    .pb-progress-running,
    .pb-progress-done {
        transition: none;
        transform: scaleX(1);
    }
}

.pb-loading {
    position: relative;
    pointer-events: none;
    opacity: 0.6;
    transition: opacity 0.3s;
}

/* Spinner classique */
.pb-loading-spinner::after {
    content: "";
    position: absolute;
    top: calc(50% - 0.5em);
    left: calc(50% - 0.5em);
    width: 1em;
    height: 1em;
    border: 2px solid #3498db;
    border-radius: 50%;
    border-top-color: transparent;
    animation: blade-spin 0.6s linear infinite;
}

/* Dots loading */
.pb-loading-dots::after {
    content: "...";
    position: absolute;
    animation: blade-dots 1.5s infinite;
    font-weight: bold;
    letter-spacing: 2px;
}

/* Pulse loading */
.pb-loading-pulse::after {
    content: "";
    position: absolute;
    top: calc(50% - 0.5em);
    left: calc(50% - 0.5em);
    width: 1em;
    height: 1em;
    background: #3498db;
    border-radius: 50%;
    animation: blade-pulse 1s ease-in-out infinite;
}

/* Progress bar */
.pb-loading-progress::after {
    content: "";
    position: absolute;
    bottom: 0;
    left: 0;
    height: 2px;
    background: #3498db;
    animation: blade-progress 1s ease-in-out infinite;
}

/* Skeleton loading */
.pb-loading-skeleton {
    background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
    background-size: 200% 100%;
    animation: blade-skeleton 1.5s infinite;
}

/* Fade loading */
.pb-loading-fade {
    animation: blade-fade 1s ease-in-out infinite alternate;
}

/* Bounce loading */
.pb-loading-bounce::after {
    content: "";
    position: absolute;
    top: calc(50% - 0.25em);
    left: calc(50% - 0.25em);
    width: 0.5em;
    height: 0.5em;
    background: #3498db;
    border-radius: 50%;
    animation: blade-bounce 0.5s cubic-bezier(0.19, 1, 0.22, 1) infinite alternate;
}

/* Overlay avec différents styles */
.pb-loading-overlay {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(255, 255, 255, 0.7);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1000;
}

.pb-loading-overlay.blur {
    backdrop-filter: blur(3px);
}

.pb-loading-overlay.dark {
    background: rgba(0, 0, 0, 0.5);
}

/* Button loading styles */
.pb-loading-button {
    position: relative;
    padding-right: 2.5em;
}

.pb-loading-button::after {
    content: "";
    position: absolute;
    right: 0.75em;
    top: calc(50% - 0.5em);
    width: 1em;
    height: 1em;
    border: 2px solid currentColor;
    border-radius: 50%;
    border-top-color: transparent;
    animation: blade-spin 0.6s linear infinite;
}

/* Animations */
@keyframes blade-spin {
    to { transform: rotate(360deg); }
}

@keyframes blade-dots {
    0%, 20% { content: "."; }
    40% { content: ".."; }
    60%, 100% { content: "..."; }
}

@keyframes blade-pulse {
    0% { transform: scale(0.8); opacity: 0.5; }
    100% { transform: scale(1.2); opacity: 0; }
}

@keyframes blade-progress {
    0% { width: 0; }
    50% { width: 100%; }
    100% { width: 0; }
}

@keyframes blade-skeleton {
    0% { background-position: 200% 0; }
    100% { background-position: -200% 0; }
}

@keyframes blade-fade {
    0% { opacity: 0.4; }
    100% { opacity: 0.8; }
}

@keyframes blade-bounce {
    from { transform: translateY(-50%); }
    to { transform: translateY(50%); }
}

/*
 * pb:cloak hides an element until PyBlade is up.
 *
 * A component is markup before it is anything else, so what it renders is on
 * the page and painted before any script has run. An element that only makes
 * sense once a component is driving it -- one the reader would see flash in a
 * half-built state -- carries pb:cloak and is hidden by this rule until the
 * directive takes the attribute off, which happens as the component is built.
 */
[pb\:cloak] {
    display: none !important;
}

/*
 * The question pb:confirm asks, when the page provides no dialogue of its own.
 *
 * Everything here is driven by custom properties, so a project restyles it by
 * setting them rather than by overriding the rules:
 *
 *   :root {
 *       --pb-confirm-background: #111;
 *       --pb-confirm-color: #fff;
 *       --pb-confirm-accept: #e11d48;
 *   }
 *
 * Replacing it altogether is a matter of putting an element marked
 * pb-confirm-dialogue in the page, built from the stub that ships with PyBlade:
 * the dialogue below is only built when the page provides none.
 */
:root {
    --pb-confirm-background: #fff;
    --pb-confirm-color: #1f2937;
    --pb-confirm-accept: #2563eb;
    --pb-confirm-accept-color: #fff;
    --pb-confirm-radius: 10px;
    --pb-confirm-width: 22rem;
    --pb-confirm-backdrop: rgba(15, 23, 42, 0.45);
}

dialog.pb-confirm {
    width: var(--pb-confirm-width);
    max-width: calc(100vw - 2rem);
    padding: 0;
    border: 0;
    border-radius: var(--pb-confirm-radius);
    background: var(--pb-confirm-background);
    color: var(--pb-confirm-color);
    box-shadow: 0 10px 40px rgba(0, 0, 0, 0.25);
}

dialog.pb-confirm::backdrop {
    background: var(--pb-confirm-backdrop);
}

dialog.pb-confirm .pb-confirm-body {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    padding: 1.25rem;
    margin: 0;
}

dialog.pb-confirm .pb-confirm-message {
    margin: 0;
    font-size: 1rem;
    line-height: 1.5;
}

dialog.pb-confirm .pb-confirm-prompt {
    padding: 0.5rem 0.625rem;
    border: 1px solid rgba(100, 116, 139, 0.4);
    border-radius: calc(var(--pb-confirm-radius) - 4px);
    font: inherit;
}

dialog.pb-confirm .pb-confirm-actions {
    display: flex;
    justify-content: flex-end;
    gap: 0.5rem;
}

dialog.pb-confirm button {
    padding: 0.5rem 0.9rem;
    border: 0;
    border-radius: calc(var(--pb-confirm-radius) - 4px);
    font: inherit;
    cursor: pointer;
}

dialog.pb-confirm .pb-confirm-cancel {
    background: transparent;
    color: inherit;
}

dialog.pb-confirm .pb-confirm-accept {
    background: var(--pb-confirm-accept);
    color: var(--pb-confirm-accept-color);
}

dialog.pb-confirm .pb-confirm-accept[disabled] {
    opacity: 0.5;
    cursor: not-allowed;
}

/*
 * The skeleton a lazy component shows while it is being got ready.
 *
 * Everything here is driven by custom properties too, so a project that wants
 * darker bars, squarer corners or no shimmer at all sets them rather than
 * overriding the rules:
 *
 *   :root {
 *       --pb-skeleton-color: #2a2a2a;
 *       --pb-skeleton-highlight: #3a3a3a;
 *       --pb-skeleton-radius: 2px;
 *   }
 *
 * Replacing it altogether is a matter of styling .pb-skeleton from scratch, or
 * of writing a placeholder() on the component and drawing whatever it should
 * look like instead.
 */
:root {
    --pb-skeleton-color: #e9ecef;
    --pb-skeleton-highlight: #f6f8fa;
    --pb-skeleton-radius: 4px;
    --pb-skeleton-height: 0.85rem;
    --pb-skeleton-gap: 0.6rem;
    --pb-skeleton-block-height: 8rem;
    --pb-skeleton-duration: 1.4s;
}

.pb-skeleton {
    display: flex;
    flex-direction: column;
    gap: var(--pb-skeleton-gap);
}

.pb-skeleton-line,
.pb-skeleton-block,
.pb-skeleton-cell {
    border-radius: var(--pb-skeleton-radius);

    /* The shimmer is a highlight travelling across a flat bar, which is one
       background rather than an element of its own to position and hide */
    background-color: var(--pb-skeleton-color);
    background-image: linear-gradient(
        90deg,
        transparent 0%,
        var(--pb-skeleton-highlight) 50%,
        transparent 100%
    );
    background-size: 200% 100%;
    background-repeat: no-repeat;
    animation: pb-skeleton-shimmer var(--pb-skeleton-duration) ease-in-out infinite;
}

.pb-skeleton-line {
    height: var(--pb-skeleton-height);
}

.pb-skeleton-block {
    height: var(--pb-skeleton-block-height);
}

.pb-skeleton-row {
    display: flex;
    gap: var(--pb-skeleton-gap);
}

.pb-skeleton-cell {
    flex: 1;
    height: var(--pb-skeleton-height);
}

@keyframes pb-skeleton-shimmer {
    from { background-position: 150% 0; }
    to { background-position: -50% 0; }
}

/* A reader who has asked for less movement gets bars that simply sit there:
   the point is to show that something is coming, not to animate at them */
@media (prefers-reduced-motion: reduce) {
    .pb-skeleton-line,
    .pb-skeleton-block,
    .pb-skeleton-cell {
        animation: none;
        background-image: none;
    }
}

/*
 * The dialogue the PyBlade error page is shown in while developing.
 *
 * It is only ever built when something has gone wrong, and only ever while
 * developing: the server sends no page in production, so none of this appears
 * on a page a reader will see.
 */
:root {
    --pb-error-width: min(72rem, 92vw);
    --pb-error-height: min(46rem, 86vh);
    --pb-error-radius: 12px;
    --pb-error-backdrop: rgba(2, 6, 23, 0.72);
}

dialog.pb-error {
    width: var(--pb-error-width);
    height: var(--pb-error-height);
    max-width: var(--pb-error-width);
    padding: 0;
    border: 0;
    border-radius: var(--pb-error-radius);
    overflow: hidden;
    background: transparent;
}

dialog.pb-error::backdrop {
    background: var(--pb-error-backdrop);
}

dialog.pb-error .pb-error-page {
    width: 100%;
    height: 100%;
    border: 0;
    display: block;
    border-radius: var(--pb-error-radius);
    background: #0f172b;
}

dialog.pb-error .pb-error-close {
    position: absolute;
    top: 0.5rem;
    right: 0.75rem;
    z-index: 1;
    width: 2rem;
    height: 2rem;
    padding: 0;
    border: 0;
    border-radius: 50%;
    font: inherit;
    font-size: 1.25rem;
    line-height: 1;
    color: #e2e8f0;
    background: rgba(15, 23, 43, 0.85);
    cursor: pointer;
}

dialog.pb-error .pb-error-close:hover {
    background: rgba(15, 23, 43, 1);
}
