/* =====================================================================
   Ticker bars  —  public/user/css/ticker.css

   Every value an admin can change arrives as a --sbt-* custom property,
   emitted inline on the bar by App\Support\Ticker::cssVars(). Each is read
   here with the shipped value as its fallback, so this stylesheet alone
   renders a complete, usable bar and the admin's choices only layer on top.

   The animation contract, shared with public/user/js/ticker.js:

     .sbt__viewport  clips
     .sbt__track     the moving element; JS sets --sbt-shift (one group + one
                     gap) and --sbt-dur (shift / speed) on it
     .sbt__group     one full pass of the items; JS clones it until the track
                     more than fills the viewport

   Because every group is identical, translating by exactly one group + one
   gap and restarting lands on a pixel-identical frame — that is what makes
   the loop seamless. Nothing here may depend on how many clones exist.
   ===================================================================== */

.sbt {
    --sbt-bg: #131413;
    --sbt-fg: #ffffff;
    --sbt-link: #ffffff;
    --sbt-link-hover: #ff9811;
    --sbt-h: auto;
    --sbt-py: 12px;
    --sbt-px: 20px;
    --sbt-my: 0px;
    --sbt-bw: 0px;
    --sbt-bs: solid;
    --sbt-bc: #1f2937;
    --sbt-radius: 0px;
    --sbt-shadow: none;
    --sbt-maxw: none;
    --sbt-justify: flex-start;
    --sbt-fs: 13px;
    --sbt-fw: 600;
    --sbt-ls: 0px;
    --sbt-lh: 1.4;
    --sbt-tt: none;
    --sbt-fstyle: normal;
    --sbt-icon-gap: 10px;
    --sbt-sep-color: currentColor;
    --sbt-sep-size: 12px;
    --sbt-gap: 40px;
    --sbt-fade-delay: 4s;
    --sbt-z: 60;
    /* Set by ticker.js once it has measured a group. Until then the track
       simply does not move, which is the correct no-JS resting state. */
    --sbt-shift: 0px;
    --sbt-dur: 0s;

    position: relative;
    z-index: var(--sbt-z);
    background: var(--sbt-bg);
    color: var(--sbt-fg);
    border-style: var(--sbt-bs);
    border-color: var(--sbt-bc);
    border-width: var(--sbt-bw);
    border-radius: var(--sbt-radius);
    box-shadow: var(--sbt-shadow);
    margin: var(--sbt-my) auto;
    max-width: var(--sbt-maxw);
    /* A radius is only visible if the bar clips what overflows it. */
    overflow: hidden;
    isolation: isolate;
}

/* Which edges the border is drawn on. Written as overrides of the shorthand
   above so switching sides can never also change the width. */
.sbt--border-none   { border-width: 0; }
.sbt--border-top    { border-width: var(--sbt-bw) 0 0; }
.sbt--border-bottom { border-width: 0 0 var(--sbt-bw); }
.sbt--border-y      { border-width: var(--sbt-bw) 0; }

.sbt--w-boxed,
.sbt--w-custom { width: 100%; }

/* Sticky / fixed placements. `position` is on the bar itself rather than a
   wrapper so the admin's z-index applies to the same element the background
   is painted on. */
.sbt--sticky {
    position: sticky;
    top: 0;
}

.sbt--bottom_fixed {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    margin-bottom: var(--sbt-my);
}

.sbt__inner {
    display: flex;
    align-items: center;
    gap: 12px;
    min-height: var(--sbt-h);
    padding: var(--sbt-py) var(--sbt-px);
    box-sizing: border-box;
}

.sbt--w-boxed .sbt__inner,
.sbt--w-custom .sbt__inner {
    max-width: var(--sbt-maxw);
    margin-inline: auto;
    width: 100%;
}

/* The clipping window. min-width:0 is what lets it shrink inside the flex
   row instead of pushing the close button off the end. */
.sbt__viewport {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
}

.sbt__track {
    display: flex;
    align-items: center;
    gap: var(--sbt-gap);
    width: max-content;
    will-change: transform;
}

.sbt__group {
    display: flex;
    align-items: center;
    gap: var(--sbt-gap);
    flex: 0 0 auto;
}

/* ------------------------------------------------------------------ items */

.sbt__item {
    display: inline-flex;
    align-items: center;
    gap: var(--sbt-icon-gap);
    flex: 0 0 auto;
    white-space: nowrap;
    color: var(--sbt-item-fg, var(--sbt-fg));
    font-family: var(--sbt-ff, inherit);
    font-size: var(--sbt-fs);
    font-weight: var(--sbt-fw);
    font-style: var(--sbt-fstyle);
    letter-spacing: var(--sbt-ls);
    line-height: var(--sbt-lh);
    text-transform: var(--sbt-tt);
    text-decoration: none;
}

a.sbt__item {
    color: var(--sbt-item-fg, var(--sbt-link));
    transition: color .18s ease, opacity .18s ease;
}

a.sbt__item:hover,
a.sbt__item:focus-visible {
    color: var(--sbt-link-hover);
    text-decoration: underline;
    text-underline-offset: 3px;
}

/* An item given its own background becomes a chip, so the colour has a shape
   to sit in rather than bleeding into the bar. */
.sbt__item--chip {
    background: var(--sbt-item-bg);
    padding: .25em .7em;
    border-radius: 999px;
}

.sbt__item--icon-after { flex-direction: row; }

.sbt__icon {
    flex: 0 0 auto;
    font-size: 1em;
    line-height: 1;
}

.sbt__icon--img {
    width: 1.25em;
    height: 1.25em;
    object-fit: contain;
    display: block;
}

.sbt__text { display: inline-block; }

/* -------------------------------------------------------------- separator */

.sbt__sep {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--sbt-sep-color);
    font-size: var(--sbt-sep-size);
    line-height: 1;
    opacity: .9;
}

.sbt__sep-dot {
    width: var(--sbt-sep-size);
    height: var(--sbt-sep-size);
    border-radius: 50%;
    background: currentColor;
    display: block;
}

.sbt__sep-line {
    width: 1px;
    height: var(--sbt-sep-size);
    background: currentColor;
    display: block;
}

.sbt__sep-star { width: 1em; height: 1em; display: block; }

.sbt__sep-img {
    width: var(--sbt-sep-size);
    height: var(--sbt-sep-size);
    object-fit: contain;
    display: block;
}

/* ------------------------------------------------------------ dismiss btn */

.sbt__close {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: transparent;
    color: inherit;
    opacity: .65;
    cursor: pointer;
    transition: opacity .15s ease, background .15s ease;
}

.sbt__close:hover,
.sbt__close:focus-visible { opacity: 1; background: rgba(255, 255, 255, .14); }

/* ================================================================ SCROLL */

@keyframes sbt-scroll-left {
    from { transform: translate3d(0, 0, 0); }
    to   { transform: translate3d(calc(-1 * var(--sbt-shift)), 0, 0); }
}

@keyframes sbt-scroll-right {
    from { transform: translate3d(calc(-1 * var(--sbt-shift)), 0, 0); }
    to   { transform: translate3d(0, 0, 0); }
}

/* The animation is applied only once JS has marked the bar ready — before
   that --sbt-shift is 0 and animating would just flicker in place. */
.sbt--anim-scroll.is-ready .sbt__track {
    animation-name: sbt-scroll-left;
    animation-duration: var(--sbt-dur);
    animation-timing-function: linear;
    animation-iteration-count: var(--sbt-iterations, infinite);
    animation-fill-mode: both;
}

.sbt--anim-scroll.sbt--dir-right.is-ready .sbt__track { animation-name: sbt-scroll-right; }

/* Pause. :hover and :focus-within are separate rules so a bar that pauses on
   focus but not on hover behaves exactly as configured. */
.sbt--anim-scroll[data-sbt-pause="1"]:hover .sbt__track { animation-play-state: paused; }
.sbt--anim-scroll[data-sbt-focus="1"]:focus-within .sbt__track { animation-play-state: paused; }

/* ================================================================== FADE */

/* One item at a time, cross-fading in place. The track stops being a moving
   strip and becomes a stack, so every group but the first is removed by JS. */
.sbt--anim-fade .sbt__viewport { display: flex; justify-content: var(--sbt-justify); }
.sbt--anim-fade .sbt__track { animation: none; width: 100%; justify-content: var(--sbt-justify); }

.sbt--anim-fade .sbt__group {
    display: grid;
    width: 100%;
    justify-items: var(--sbt-justify);
}

/* Every item occupies the same grid cell; only the active one is painted. */
.sbt--anim-fade .sbt__item {
    grid-area: 1 / 1;
    opacity: 0;
    visibility: hidden;
    transform: translateY(4px);
    transition: opacity .45s ease, transform .45s ease, visibility 0s linear .45s;
}

.sbt--anim-fade .sbt__item.is-active {
    opacity: 1;
    visibility: visible;
    transform: none;
    transition: opacity .45s ease, transform .45s ease;
}

/* Separators mean nothing when only one item shows at a time. */
.sbt--anim-fade .sbt__sep { display: none; }

/* ================================================================ STATIC */

.sbt--anim-none .sbt__viewport { overflow: visible; }

.sbt--anim-none .sbt__track {
    animation: none;
    width: 100%;
    flex-wrap: wrap;
    justify-content: var(--sbt-justify);
    row-gap: calc(var(--sbt-gap) / 4);
}

.sbt--anim-none .sbt__group { flex-wrap: wrap; row-gap: calc(var(--sbt-gap) / 4); }

/* ====================================================== REDUCED MOTION */

/*
   A visitor who has asked for less motion gets a bar that does not move. The
   content must still be reachable, so the viewport becomes scrollable rather
   than clipping the items that were going to scroll into view.

   Gated on data-sbt-reduced="1" — an admin can switch the courtesy off for a
   bar whose whole purpose is the movement, and it is on by default.
*/
@media (prefers-reduced-motion: reduce) {
    .sbt[data-sbt-reduced="1"] .sbt__track { animation: none !important; }
    .sbt[data-sbt-reduced="1"] .sbt__viewport { overflow-x: auto; scrollbar-width: thin; }
    .sbt[data-sbt-reduced="1"] .sbt__item { transition: none !important; }
    .sbt[data-sbt-reduced="1"].sbt--anim-fade .sbt__item { opacity: 1; visibility: visible; transform: none; }
    .sbt[data-sbt-reduced="1"].sbt--anim-fade .sbt__group { display: flex; gap: var(--sbt-gap); }
    .sbt[data-sbt-reduced="1"].sbt--anim-fade .sbt__sep { display: inline-flex; }
}

/* Print: a scrolling strip is meaningless on paper, but its wording may not
   be — lay the items out as a static row. */
@media print {
    .sbt__track { animation: none !important; transform: none !important; flex-wrap: wrap; }
    .sbt__viewport { overflow: visible; }
    .sbt__item { opacity: 1 !important; visibility: visible !important; }
    .sbt__close { display: none; }
}
