/* the announcement bar above the header
   Part of the split style.css -- see the note at the top of 00-base.css.
   Each file is its own <link> and is parsed alone, so every construct in
   here must open and close inside this file.

   Numbered 05 so it sits between the palette in 00-base.css, whose tokens it
   uses, and 10-header.css, whose element it sits above. */

/* Fixed from the first render, and that is the whole point of the number
   being here rather than in a padding. The site's Lighthouse CLS is 0: a bar
   that appeared late, or grew when its real message arrived, would spend that
   on decoration. Both variants of every message are in the document from the
   first byte and the container never measures anything, so the only thing
   that can change this height is an edit to these two lines.

   36px on desktop reads as a system message: present, above the navigation,
   and not competing with it. 40px on a phone because the tap target for the
   dismiss button has to fit and 36 does not leave room for it beside a line
   of text. */
:root { --pet-announcement-height: 36px; }

.pet-announcement {
  background: var(--pet-primary-soft);
  /* The transparent top rule is not decoration: the offer variant below
     paints a 2px accent rule there, and a border that only exists in one
     state is a 2px layout shift the moment the state resolves. Reserving it
     in every state -- with box-sizing so the borders come out of the fixed
     height rather than adding to it -- means the two variants are the same
     box and the swap moves nothing. */
  border-top: 2px solid transparent;
  border-bottom: 1px solid var(--pet-border);
  box-sizing: border-box;
  color: var(--pet-text);
  font-size: .85rem;
  height: var(--pet-announcement-height);
  line-height: 1.3;
  width: 100%;
}

/* Never laid out at all, rather than laid out and then hidden. The <head>
   script in inc/announcement-bar.php adds this class before <body> is parsed,
   so a bar dismissed earlier in the session costs no space and no shift on
   the pages that follow. */
.pet-announcement-dismissed .pet-announcement { display: none; }

/* The second reason a bar is not laid out: the person reading is a registered
   customer, and the state they would be in is one the shop does not show them
   (today that is the member sentence and not a running offer -- see
   inc/announcement-bar.php). Same <head> script, same pre-paint timing, and a
   separate class rather than a reuse of the one above, because the two
   decisions are made from different storage, expire differently and are undone
   by different things: dismissal is this session and this message, this one is
   this browser until it is signed out. Sharing a class would make "the bar
   came back" impossible to explain. */
.pet-announcement-signed-in .pet-announcement { display: none; }

.pet-announcement-inner {
  align-items: center;
  display: flex;
  gap: .5rem;
  height: 100%;
  justify-content: center;
  margin: 0 auto;
  max-width: 1280px;
  padding: 0 clamp(.75rem, 3vw, 1.5rem);
  position: relative;
}

/* One line, always. A message that wrapped would either grow the bar or be
   clipped mid-word, and on the narrow screens where wrapping starts there is
   a short line written for exactly that reason. */
.pet-announcement-message {
  align-items: center;
  display: flex;
  flex-wrap: nowrap;
  gap: .4rem;
  margin: 0;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pet-announcement-message > span,
.pet-announcement-message > a {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* --pet-primary on --pet-primary-soft measures 6.39, so the link clears AA
   for small text on the calm ground with room to spare. Underlined rather
   than left to colour alone: colour is not the only thing that may
   distinguish a link (WCAG 1.4.1), and at .85rem a green word against green
   is not a strong enough signal on its own. */
.pet-announcement-link {
  color: var(--pet-primary);
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 2px;
}

.pet-announcement-link:hover { color: var(--pet-primary-hover); }

/* The guest line's two account links stop shrinking.
   At 390px the row wants ~397px for a 311px lane, and flexbox was taking the
   difference out of everything with overflow: hidden on it -- so "Войти"
   rendered as "Во…" and "Регистрация" as "Регистр…". A sentence that loses
   its last two words is still a sentence; a control clipped to two letters is
   not a control, and there is no shorter true wording for "Войти" to fall
   back to. So the links keep their intrinsic width and the sentence beside
   them absorbs the shortfall -- which is what the running line below is for.

   :not(:first-child) is what separates the two kinds of link this class
   covers. In the guest variant the links follow the sentence. In the offer
   variant the single link *is* the sentence -- it wraps the full and short
   spans -- and that one must go on shrinking, or it would push the bar wider
   than the screen. */
.pet-announcement-message > .pet-announcement-link:not(:first-child) { flex: none; }

/* The separators between the sentence and the two links. Decoration and
   aria-hidden in the markup, so --pet-text-secondary (5.41 on the calm
   ground, 5.14 on the warm one) is comfortably more than they owe. */
.pet-announcement-sep {
  color: var(--pet-text-secondary);
  flex: none;
}

/* Absolutely positioned so the message stays centred on the bar rather than
   being pushed off-centre by the button's width, and so the button occupies
   no space in the line the message has to fit into.
   inset-inline-end, not right: the Hebrew bar carries dir="rtl" and the
   button belongs on the side the reading ends, which is the left there. */
.pet-announcement-dismiss {
  align-items: center;
  background: none;
  border: 0;
  border-radius: 50%;
  color: var(--pet-text-secondary);
  cursor: pointer;
  display: flex;
  height: 28px;
  inset-inline-end: clamp(.25rem, 2vw, .75rem);
  justify-content: center;
  padding: 0;
  position: absolute;
  width: 28px;
}

.pet-announcement-dismiss svg {
  fill: currentColor;
  height: 10px;
  width: 10px;
}

.pet-announcement-dismiss:hover { color: var(--pet-text); }

/* The focus ring is drawn inside the bar rather than around it: at 36px tall
   an outline-offset of 3px would be clipped by the header below. --pet-focus
   measures 10.53 on the calm ground and 10.02 on the warm one. */
.pet-announcement-dismiss:focus-visible {
  outline: 3px solid var(--pet-focus);
  outline-offset: -1px;
}

/* The offer variant. --pet-accent (#C98A45) is a fill and a decoration and
   never a foreground -- it measures 2.39 on its own soft ground, and
   tests/palette-contrast.php fails the build structurally if it appears in a
   colour, fill or stroke property anywhere in these stylesheets. So the warm
   accent the owner specified is carried by the top rule and nothing else,
   while the text that has to be read uses --pet-accent-text (#865A28), which
   measures 4.91 on --pet-accent-soft. Same intent, two tones, because one
   tone cannot do both jobs. */
.pet-announcement:has(.pet-announcement-message[data-announcement-state="offer"]:not([hidden])) {
  background: var(--pet-accent-soft);
  border-top-color: var(--pet-accent);
  border-bottom-color: var(--pet-accent);
}

.pet-announcement:has(.pet-announcement-message[data-announcement-state="offer"]:not([hidden])) .pet-announcement-link {
  color: var(--pet-accent-text);
}

/* Desktop shows the full sentence, a phone shows the short line, and the
   swap is a media query rather than a measurement so it costs nothing after
   paint. Both strings are translated separately in inc/i18n.php: the short
   one is written to fit, not truncated from the long one. */
.pet-announcement-short { display: none; }

@media (max-width: 719px) {
  :root { --pet-announcement-height: 40px; }

  .pet-announcement { font-size: .8rem; }
  .pet-announcement-full { display: none; }
  .pet-announcement-short { display: inline; }

  /* The inner row stops centring and starts at the reading edge: a centred
     line that then has to ellipsis loses its beginning as well as its end. */
  .pet-announcement-inner {
    justify-content: flex-start;
    padding-inline-end: 2.25rem;
  }
}

/* ---------------------------------------------------------------------------
   THE RUNNING LINE
   ---------------------------------------------------------------------------

   The short line is written to fit and, at the widths this site was checked
   at, it did. It does not fit at 390px: measured on the shipped page, the
   Russian short line alone wants 246px, the separators and the two account
   links want another 119px plus 32px of gaps, and the row is 311px wide. The
   browser's answer was an ellipsis on every one of them at once.

   The links were dealt with above. What is left over is a sentence that is
   genuinely longer than the space there is, and the owner asked for it to run
   rather than be cut. So: the lane is whatever the links leave, the track
   inside it carries the sentence twice, and one animation step moves the
   track by exactly one copy -- at which point the second copy is standing
   where the first one was and the loop repeats with no seam and no jump back.

   Nothing here animates on its own. assets/announcement-bar.js measures the
   visible line, and only if it is actually being clipped does it build this
   structure and add .is-marquee. A message that fits is left alone, a page
   with no JavaScript keeps today's ellipsis, and neither costs any motion.
   The distance and the duration arrive from that measurement as custom
   properties, which is what makes the speed a constant ~25px per second
   instead of something that depends on how long the sentence happens to be:
   "slow" has to mean the same thing in Russian, Hebrew and English.

   The distance is signed, not a percentage, because the Hebrew bar is
   dir="rtl" and its track has to travel the other way. The script reads the
   resolved direction and hands over a positive number there.

   Note the track is not laid out at all until the class is on: the message
   keeps its fixed height either way, so switching this on after paint moves
   nothing on the page. That is the one property this component is not allowed
   to spend. */
.pet-announcement-message > .pet-announcement-marquee {
  flex: 1 1 auto;
  min-width: 0;
  /* No ellipsis on the lane: the sentence is not being truncated here, it is
     being scrolled, and a fading "…" riding along at the edge would say the
     opposite. The clipping itself is inherited from the > span rule above. */
  text-overflow: clip;
}

.pet-announcement-marquee-track {
  display: flex;
  width: max-content;
}

/* Both copies: the original element and the aria-hidden clone beside it.
   The display declaration is here to win against the two rules that decide
   which of the pair is on screen at this width -- inside the track the answer
   is "both of them, always", because the track only ever holds copies of the
   one line that is already the right one for this width. */
.pet-announcement-marquee-track > .pet-announcement-full,
.pet-announcement-marquee-track > .pet-announcement-short {
  display: block;
  flex: none;
  /* The blank the reader sees between the end of the sentence and its next
     pass. Part of the copy's own width, so it is part of the distance the
     track travels and the loop stays seamless. */
  padding-inline-end: 3rem;
}

.pet-announcement-message.is-marquee .pet-announcement-marquee-track {
  animation: pet-announcement-marquee var(--pet-announcement-marquee-duration, 14s) linear infinite;
}

/* A ticker that cannot be stopped is hard to read a long word off, and the
   bar carries two focusable links. Pausing under the pointer and while
   anything inside has keyboard focus costs nothing and is the difference
   between a line you can read and one you have to wait out. */
.pet-announcement:hover .pet-announcement-marquee-track,
.pet-announcement:focus-within .pet-announcement-marquee-track {
  animation-play-state: paused;
}

@keyframes pet-announcement-marquee {
  from { transform: translateX(0); }
  to { transform: translateX(var(--pet-announcement-marquee-distance, 0)); }
}

/* The registration column on /my-account/, which the guest bar's "Register"
   link anchors to. Same reasoning as #top and #pet-main in 00-base.css: it is
   a jump target rather than a control, it carries tabindex="-1" so the
   reading position follows the link, and the browser's default ring around a
   whole form column reads as a stray rule rather than as a focus indicator.
   The focus still lands; it just is not drawn. */
#pet-register:focus { outline: 0; }

/* The running line above is the one animation this bar has, and under this
   preference it must not exist rather than merely be paused. The script
   checks the same query and does not build the track at all, so in practice
   nothing reaches these rules; they stay as the backstop for the case the
   preference is turned on after the page was built, and for anything added to
   this bar later, which then inherits the preference instead of having to
   remember it. */
@media (prefers-reduced-motion: reduce) {
  .pet-announcement,
  .pet-announcement * {
    animation: none !important;
    transition: none !important;
  }

  /* With the animation gone the track would sit at its start position and
     show the sentence cut off mid-word with no ellipsis, which is worse than
     what was there before. Collapse the pair back to one line that ellipses,
     which is exactly the no-JavaScript rendering. */
  .pet-announcement-marquee-track {
    display: block;
    width: 100%;
  }

  .pet-announcement-marquee-track > [aria-hidden="true"] { display: none; }

  .pet-announcement-marquee-track > .pet-announcement-full,
  .pet-announcement-marquee-track > .pet-announcement-short {
    overflow: hidden;
    padding-inline-end: 0;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}
