/* Prevent font flicker by ensuring fonts load before rendering text */
html {
  transition: opacity 0.1s ease-in;
  /* Without this, mobile browsers/WebViews can promote #scroll-container
     (see Layouts/Navbar.elm) to act as the page's root scroller and apply
     native rubber-band/pull-to-refresh translation to the whole view,
     dragging fixed-position navbar elements along with it. */
  overscroll-behavior-y: none;
  /* ZOOM — pinch stays, double-tap goes.
   *
   * `manipulation` allows scrolling and pinch-zoom and disallows
   * double-tap-to-zoom, so two quick taps on a stepper or a checkbox are two
   * taps rather than a zoom. It has to be declared on the root, because the
   * gesture is resolved against the touched element's whole ancestor chain up
   * to the viewport, and nothing below here sets it.
   *
   * Pinch used to be off too (`pan-x pan-y`, and `user-scalable=no` in the
   * viewport meta). That stopped anyone magnifying text as small as 10px, and
   * it is not ours to take away on the web (STYLE-02). The Android app is a
   * separate decision, made natively in `MainActivity` (BuildAndroid.hs),
   * where the system font size reaches the type scale instead. */
  touch-action: manipulation;
  /* TAP HIGHLIGHT — half of the "blue flash on tap" fix.
   *
   * WebKit/Blink paint a translucent blue-grey rectangle over whatever a
   * touch lands on. It covers the element's whole border box, so on a card
   * or a group row the flash is the size of the row rather than the control
   * that was tapped, and it lingers for the length of the tap. Nothing in
   * this UI wants it: every interactive surface already draws its own
   * pressed/hover treatment.
   *
   * The property inherits, so declaring it once on the root reaches every
   * element in both build targets — `static/` is copied wholesale into the
   * Android app tree, and no override shadows this file.
   *
   * The other half is elm-ui's focus ring, which its own generated
   * stylesheet paints and CSS here cannot reach — see `Style.Interaction`
   * (`noFocusStyle`, `nestedLayout`). Raw form controls: FOCUS OUTLINE. */
  -webkit-tap-highlight-color: transparent;
}

/* FOCUS OUTLINE — the browser's own focus ring on raw form controls.
 *
 * elm-ui's elements carry `.s`, and its static stylesheet already gives
 * `.s:focus` no outline. A raw `Html.input`, `textarea` or `select` (the
 * uncontrolled bars, the entry fields, the custom-entity forms) has no `.s`,
 * so the browser drew its blue ring on focus, and it stayed for as long as
 * the cursor was in the field. Some of those controls turned it off inline,
 * one at a time; this is the one rule for all of them. Fields still show
 * focus by their own means — a caret, and the frame's own border. */
input:focus,
textarea:focus,
select:focus {
  outline: none;
}

/* Uncontrolled search inputs (Option A: JS debounces keystrokes before
   notifying Elm, so the input must not be re-rendered per keystroke). The
   placeholder is the theme's secondaryVar, the hint colour the controlled
   fields use, from `Style.Interaction.rootProperties` (THEME COLOURS IN CSS
   below). */
.uncontrolled-search-input::placeholder {
  color: var(--secondary-var);
  opacity: 1;
}
.uncontrolled-search-input::-webkit-input-placeholder {
  color: var(--secondary-var);
}

/* Group-name editors etc. — uncontrolled inputs that only round-trip on
   Enter/blur via the inputCommitted port. Placeholder uses a muted tone
   instead of the bold primary color the input itself displays. */
.uncontrolled-name-input::placeholder {
  color: rgba(150, 150, 150, 0.55);
  font-weight: 400;
  opacity: 1;
}
.uncontrolled-name-input::-webkit-input-placeholder {
  color: rgba(150, 150, 150, 0.55);
  font-weight: 400;
}

body {
  font-family: "League Spartan", sans-serif;
  font-display: swap;
  overscroll-behavior-y: none;
}

/* A palette switch is one visual event. While Shared is applying a new
   light/dark or named colour theme, force every colour-bearing property to
   finish together. The class is removed after 260ms, so component-specific
   hover, focus, transform and data-visualisation timings remain intact. */
.theme-transitioning,
.theme-transitioning * {
  transition-property:
    color, background-color, border-color, box-shadow, fill, stroke !important;
  transition-duration: 240ms !important;
  transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1) !important;
  transition-delay: 0ms !important;
}

/* Scrollbar chrome is DESKTOP ONLY, and deliberately so.
 *
 * Styling `::-webkit-scrollbar` on `*` opts every scrollable element in the
 * document into Chromium's *custom* scrollbar path. On Android that costs the
 * scroller its composited fast path, so flings run on the main thread: a swipe
 * coasts, stalls against whatever else the frame is doing, and stops dead —
 * the "bumps and barriers" that read as the page catching on something. The
 * cause is cosmetic CSS, which is why it never looks like a scrolling bug.
 *
 * These rules used to apply at every width, with a second, later
 * `*::-webkit-scrollbar { display: none }` cancelling the visible bar again —
 * so mobile paid the compositing penalty and did not even get a scrollbar for
 * it. Below 1200px we now say nothing about scrollbars at all, which leaves
 * the WebView on its native overlay ones.
 *
 * Ported from compatIQ (`fix: scrollbars`, d99594af), where this exact fix
 * landed and was never carried across to this package. */
@media (min-width: 1200px) {
  * {
    scrollbar-width: thin;
    scrollbar-color: var(--scrollbar-thumb, rgb(120, 60, 0))
      var(--scrollbar-track, rgb(20, 20, 20));
  }

  *::-webkit-scrollbar {
    width: 10px;
    height: 10px;
    display: block;
  }

  *::-webkit-scrollbar-track {
    background: var(--scrollbar-track, rgb(30, 30, 30));
    border-radius: 4px;
  }

  *::-webkit-scrollbar-thumb {
    background: var(--scrollbar-thumb, rgb(120, 60, 0));
    border-radius: 4px;
  }

  *::-webkit-scrollbar-thumb:hover {
    background: var(--scrollbar-thumb, rgb(120, 60, 0));
  }

  *::-webkit-scrollbar-corner {
    background: var(--scrollbar-track, rgb(30, 30, 30));
  }
}

@media (max-width: 1199px) {
  * {
    scrollbar-width: none;
  }

  *::-webkit-scrollbar {
    display: none;
  }
}

/* SCROLL FADE: a list that scrolls inside a dialog fades out at an end with
   more beyond it, in place of a scrollbar (`Ui.Overlay.AddDialog`,
   `data-scroll-fade`). `Ui/ScrollFade.ts` marks the ends, `data-more-above`
   and `data-more-below`, and each fade goes as its end is reached.

   The fade is a mask, as `Components.Rolodex`'s edges are, so the rows fade
   against whatever is behind the list. Each end's opacity is a registered
   property so it can be transitioned; an engine without `@property` snaps
   instead. The mask sits on the list's box, which does not scroll, so the
   fades stay at its edges while the rows move under them. */
@property --scroll-fade-top {
  syntax: "<number>";
  inherits: false;
  initial-value: 1;
}

@property --scroll-fade-bottom {
  syntax: "<number>";
  inherits: false;
  initial-value: 1;
}

[data-scroll-fade] {
  --scroll-fade-top: 1;
  --scroll-fade-bottom: 1;
  --scroll-fade-size: 28px;
  -webkit-mask-image: linear-gradient(
    to bottom,
    rgba(0, 0, 0, var(--scroll-fade-top)) 0,
    #000 var(--scroll-fade-size),
    #000 calc(100% - var(--scroll-fade-size)),
    rgba(0, 0, 0, var(--scroll-fade-bottom)) 100%
  );
  mask-image: linear-gradient(
    to bottom,
    rgba(0, 0, 0, var(--scroll-fade-top)) 0,
    #000 var(--scroll-fade-size),
    #000 calc(100% - var(--scroll-fade-size)),
    rgba(0, 0, 0, var(--scroll-fade-bottom)) 100%
  );
  transition:
    --scroll-fade-top 200ms ease-out,
    --scroll-fade-bottom 200ms ease-out;
  scrollbar-width: none;
}

[data-scroll-fade]::-webkit-scrollbar {
  display: none;
}

[data-scroll-fade][data-more-above] {
  --scroll-fade-top: 0;
}

[data-scroll-fade][data-more-below] {
  --scroll-fade-bottom: 0;
}

/* The app's primary content area reads as a native app surface, so it has no
   visible scrollbar at any width. Scoped to the one id rather than `*`, which
   keeps the custom-scrollbar penalty above off everything else. */
#scroll-container {
  scrollbar-width: none;
}

#scroll-container::-webkit-scrollbar {
  display: none;
}

/* RSVP loading spinner animation */
@keyframes rsvp-spin {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}

/* Throbber/spinner animation for loading states */
@keyframes throbber-spin {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}

/* Jiggle animation for bubble hover */
@keyframes jiggle {
  0%,
  100% {
    rotate: 0deg;
    scale: 1;
  }
  25% {
    rotate: -3deg;
    scale: 1.02;
  }
  50% {
    rotate: 3deg;
    scale: 1.04;
  }
  75% {
    rotate: -2deg;
    scale: 1.02;
  }
}

.jiggle-hover {
  transform-origin: center center;
}

.jiggle-hover:hover {
  animation: jiggle 0.3s ease-in-out;
  cursor: pointer;
}

/* A link card scales up on hover (PlanCards.hoverScale) and spills into the
   grid gap. Without this, a later sibling in the row paints over it, so the
   card lifts but slides under its neighbour. Set on the card's wrapper, which
   is the flex item the siblings are stacked against. */
.plan-card-lift:hover {
  z-index: 1;
}

/* Smooth drag and drop styles */
[data-draggable="true"] {
  transition:
    transform 0.2s cubic-bezier(0.4, 0, 0.2, 1),
    box-shadow 0.2s ease;
  cursor: grab;
  user-select: none;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
}

[data-draggable="true"]:hover {
  transform: scale(1.005);
}

[data-draggable="true"]:active {
  cursor: grabbing;
}

/* Dragged element styling */
[data-dragging="true"] {
  cursor: grabbing !important;
  pointer-events: none;
  z-index: 1000;
  box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
  transition: none !important;
}

/* Non-dragged items shift out of the way with a snappy bounce */
[data-drag-active="true"] [data-drag-index]:not([data-dragging="true"]),
[data-drag-active="true"] [data-group-drag-index]:not([data-dragging="true"]) {
  transition: transform 180ms cubic-bezier(0.34, 1.4, 0.64, 1);
}

/* Group panel hover highlight during food drag (applied by JS).
   The class is added to the outer wrapper (group-panel-*), but the rounded
   corners live on its first child (elm-ui groupContent column), so target that. */
.drag-hover > * {
  box-shadow: inset 0 0 0 2px var(--primary-color, rgba(204, 102, 0, 0.6));
}

/* Drop gap animation */
.drop-gap {
  height: 0;
  overflow: hidden;
  transition: height 0.2s cubic-bezier(0.4, 0, 0.2, 1);
}

.drop-gap.active {
  height: 60px;
}

/* ==================== CHART AXES ==================== */
.y-axis .domain,
.y-axis .tick line {
  stroke: currentColor;
}

/*
 * elm-visualization draws the y axis itself, hardcoding `font-size: 10` on
 * the group it renders, so the only place to set the tick size is here.
 *
 * Keep this in step with `plotFont` in
 * `Features/Enaychi/Ui/Chart/Shell.elm`, which sizes every *other* piece of
 * text in the plot — the date labels and the unit label. Inside an SVG a CSS
 * `px` is a user unit, the same thing `Px` is in TypedSvg, so the same number
 * means the same size in both.
 */
.y-axis .tick text {
  fill: currentColor;
  font-size: 11px;
}
/* ==================== END CHART AXES ==================== */

/* ==================== ANDROID KEYBOARD ==================== */
/*
 * `data-keyboard-open` is set on <html> by NavigationBarPlugin's window
 * insets listener (app target only; never set in a browser tab, so these
 * rules are inert on web).
 *
 * The bottom navigation tray is `position: fixed` at the bottom of the
 * viewport. When the Android IME opens it ended up sitting directly on top
 * of the keyboard, and on short screens that left almost nothing of the
 * actual view visible. There is nothing useful about a navigation tray
 * while the user is typing, so take it out of the way entirely.
 */
html[data-keyboard-open] #app-bottom-tray {
  display: none !important;
  visibility: hidden !important;
}

html[data-keyboard-open] #enaychi-tray {
  display: none !important;
  visibility: hidden !important;
  pointer-events: none !important;
}

html[data-keyboard-open] #scroll-container {
  padding-bottom: 0 !important;
}
/*
 * OVERLAY KEYBOARD. The WebView does not shrink for the keyboard (the app is
 * edge-to-edge), so the keyboard covers the lower part of a dialog's scrim,
 * and a field low in the card was typed into blind. While it is open, an open
 * scrim (`Components.Overlay.shell`) takes the keyboard's whole height as
 * bottom padding: `--ime-height` is the keyboard above the navigation bar,
 * `--sys-bottom` the bar it covers. The centred card then sits in the space
 * above it, capped to that space and scrolling inside it; interop's
 * KEYBOARD-AWARE FOCUS scrolls the focused field into view in the card.
 */
html[data-keyboard-open] [data-overlay-open="true"] {
  padding-bottom: calc(12px + var(--ime-height, 0px) + var(--sys-bottom, 0px)) !important;
}

html[data-keyboard-open] [data-overlay-open="true"] > [data-overlay-card] {
  max-height: 100% !important;
  overflow-y: auto !important;
}
/* ==================== END ANDROID KEYBOARD ==================== */

/* ==================== ENAYCHI DOCK ==================== */
/*
 * `Features/Enaychi/Ui/Shell/Dock.elm` writes the bloom's motion inline --
 * each element's open or closed transform, opacity and transition -- because
 * elm/virtual-dom cannot set custom properties to hand the numbers to a
 * stylesheet. What is left here is what inline styles cannot say.
 */

/* A closed piece is still in the DOM, waiting at its tab. elm-ui hands
   `pointer-events: auto` to every child of an `inFront` layer, which would
   beat an inherited `none`, so it is stated for every descendant.

   The collapse control is deliberately not in this list. It sits over its
   tab, and turning its hit test off the moment it starts fading put the
   pointer on the tab underneath, whose green hover halo then flashed through
   the fading red. It stays hit-testable until its own `visibility: hidden`
   lands at the end of the fade, which takes it out on its own. */
[data-bloom="closed"],
[data-bloom="closed"] *,
[data-dock-shelf="closed"],
[data-dock-shelf="closed"] *,
[data-dock-tray="closed"],
[data-dock-tray="closed"] * {
  pointer-events: none !important;
}

/* The tucked tray is still mounted for its reverse transition, but must not
   catch a touch below the viewport. */
[data-dock-tray="collapsed"],
[data-dock-tray="collapsed"] * {
  pointer-events: none !important;
}

[data-dock-landscape-destinations="closed"],
[data-dock-landscape-destinations="closed"] * {
  pointer-events: none !important;
}

/* `AppChrome.scrollContainer` reserves the full dock in its inline style.
   A tucked tray leaves only the 34px handle, so reclaim the rest for the
   page.  Chromium's `:has` is available in the Android WebView versions we
   support; the inline fallback remains safe on older browsers. */
body:has(#enaychi-tray[data-dock-collapsed="true"]) #scroll-container {
  padding-bottom: calc(var(--sys-bottom, env(safe-area-inset-bottom, 0px)) + 40px) !important;
}

/* A landscape rail owns its edge rather than the bottom of the page. */
body:has(#enaychi-tray[data-dock-landscape][data-dock-side="left"]) #scroll-container {
  padding-bottom: 0 !important;
  padding-left: 76px;
}

body:has(#enaychi-tray[data-dock-landscape][data-dock-side="right"]) #scroll-container {
  padding-bottom: 0 !important;
  padding-right: 76px;
}

/* BUBBLE PRESS — a small compression, the tap's immediate answer.
   A bubble with a standing glow is pressed by its glow wrapper
   (`data-dock-glow`, `Dock.glowing`), which shrinks the glow with the disc.
   Shrinking only the bubble left the wrapper's shadow at full size, and the
   page showed through between the two as a ring. A bubble with no wrapper
   shrinks itself. */
[data-dock-bubble]:active {
  transform: scale(0.92);
}

[data-dock-glow] [data-dock-bubble]:active {
  transform: none;
}

[data-dock-glow]:has([data-dock-bubble]:active) {
  scale: 0.92;
}

/* A greyed bubble (`Dock.greyedLook`, `aria-disabled`) does nothing, so it
   does not answer a press either. */
[data-dock-bubble][aria-disabled="true"]:active {
  transform: none;
}

[data-dock-glow]:has([data-dock-bubble][aria-disabled="true"]:active) {
  scale: none;
}

/* BUBBLE THROB — a tap's pulse, the date pill's press played through once
   (`Ui/DockFeedback.ts` sets `data-throb` on each activation and clears it on
   `animationend`). `:active` alone holds only while the finger is down, which
   a quick tap on a bubble that is about to move never showed. Played on the
   glow wrapper when there is one, for the reason BUBBLE PRESS gives. The
   `scale` property, not `transform`, so it composes with whatever moves it.
   It starts where the press left the bubble (`--throb-from`, set by
   `throb`), so a press and its throb are one pulse rather than two. */
@keyframes dock-bubble-throb {
  0% { scale: var(--throb-from, 1); filter: brightness(1); }
  40% { scale: 0.88; filter: brightness(1.15); }
  100% { scale: 1; filter: brightness(1); }
}

[data-dock-bubble][data-throb],
[data-dock-glow][data-throb] {
  animation: dock-bubble-throb 240ms cubic-bezier(0.2, 0, 0, 1);
}

@media (prefers-reduced-motion: reduce) {
  [data-dock-bubble][data-throb],
  [data-dock-glow][data-throb] {
    animation: none;
  }
}

/* BUBBLE HOVER — the glyph's hover colour (`--bubble-hover`, inline) and the
   intent's halo (`data-bubble-halo`), only on devices that really hover. A tap
   leaves `:hover` on the bubble it touched, and the halo stayed on as a ring
   that read as a border. Same 12px blur as `Interaction.haloShadow`. */
@media (hover: hover) {
  [data-dock-bubble]:hover {
    color: var(--bubble-hover) !important;
  }

  [data-dock-bubble][data-bubble-halo="go"]:hover {
    box-shadow: 0 0 12px var(--intent-go) !important;
  }

  [data-dock-bubble][data-bubble-halo="stop"]:hover {
    box-shadow: 0 0 12px var(--intent-stop) !important;
  }
}

/* Reduced motion: no travel, no spiral, no stagger. Everything simply is
   where its state puts it; the fades, shortened, are all that is left. */
@media (prefers-reduced-motion: reduce) {
  [data-dock-moving] {
    transition: none !important;
  }

  /* No waits either. Some pieces hold their state for the length of a trip
     that reduced motion no longer makes (the collapse control, the parent's
     glow), so their inline delays left the parent's slot empty for up to a
     third of a second (B-071). */
  #enaychi-tray,
  #enaychi-tray * {
    transition-delay: 0s !important;
  }

  [data-dock-fade],
  [data-dock-shelf] {
    transition: opacity 100ms linear !important;
  }

  [data-dock-bubble]:active {
    transform: none;
  }

  [data-dock-glow]:has([data-dock-bubble]:active) {
    scale: none;
  }
}
/* ==================== END ENAYCHI DOCK ==================== */

/* ==================== PAGE FADE ==================== */
/*
 * A page mounting in the tracker body (`Shell.Motion.pageFade`). Duration and
 * curve are written inline from the dock's timing; only the keyframes, which
 * inline styles cannot declare, live here. Reduced motion drops it.
 */
@keyframes enaychi-page-in {
  from {
    opacity: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  [data-page-fade] {
    animation: none !important;
  }
}
/* ==================== END PAGE FADE ==================== */

/* ==================== POPOVER ==================== */
/*
 * Mobile replacement for every hover tooltip — see
 * `Components/Popover.elm`, which documents why the tooltip variant's
 * open/closed state lives in the DOM as focus rather than in a page
 * model.
 *
 * The markup elm-ui produces for `E.inFront (el [ ..., E.below panel ])`
 * is:
 *
 *   div[data-popover=trigger][tabindex=0]   <- invisible, covers the row
 *     div.nb.b                              <- elm-ui's "below" wrapper
 *       div[data-popover=panel]
 *         div[data-popover=card]
 *
 * The panel is a *grandchild* of the trigger, hence the descendant
 * selectors. That nesting is also what makes taps inside the panel keep
 * it open: the nearest focusable ancestor of anything in there is still
 * the trigger.
 *
 * `:focus`, never `:focus-within` — the close chip works by taking focus
 * away from the trigger, and `:focus-within` would keep matching.
 */
[data-popover="trigger"],
[data-popover="close"] {
  outline: none;
  -webkit-tap-highlight-color: transparent;
}

[data-popover="panel"] {
  visibility: hidden;
  opacity: 0;
  /* Hidden panels must not swallow taps meant for the row underneath. */
  pointer-events: none;
  z-index: 1000;
  transition: opacity 120ms ease;
}

/*
 * elm-ui's nearby wrapper is `pointer-events: none`; re-enabling it here
 * is what lets the user scroll a tall popover and hit the close chip.
 */
[data-popover="trigger"]:focus [data-popover="panel"] {
  visibility: visible;
  opacity: 1;
  pointer-events: auto;
}

[data-popover="card"] {
  /* Never wider than the viewport, however the panel is anchored. */
  max-width: calc(100vw - 24px);
  /* Tall content (the nutrient / muscle target cards) scrolls in place
   * instead of running off the bottom of the screen. */
  max-height: 60vh;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}
/* ==================== END POPOVER ==================== */

/* ==================== TOOLTIP CLAMP ==================== */
/*
 * The pointer-device counterpart of the popover clamp above, for the hover
 * cards in `Components/ToolTip.elm` and `Ui/Overlay/TargetTooltip.elm`.
 *
 * Those cards hang off the row with elm-ui's `below`, which is an
 * unconditional `top: 100%` — so a row low in the viewport (the last
 * nutrients of a bar section, say) put the card's bottom off the bottom of
 * the screen, with no way to scroll to it: the card is inside a
 * `pointer-events: none` wrapper and only exists while the row is hovered.
 *
 * Nothing in CSS can see that overflow, so `--tooltip-shift-y` is written by
 * the hover handler in `src/interop.ts`, which measures the card and nudges
 * it back up. The offset is an inset rather than a `transform` on purpose:
 * `Style.Transitions.transition` animates `transform`, and the card is
 * already fading in when the offset is applied — it would visibly slide into
 * place from wherever the previous hover left it.
 *
 * `max-height` is the backstop for a card taller than the viewport (a
 * nutrient with a long food breakdown), which no amount of shifting can fit.
 */
[data-tooltip="card"] {
  position: relative;
  top: var(--tooltip-shift-y, 0px);
  max-height: calc(100vh - 16px);
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* Right-facing info cards can overlap the next surface card. Raise only the
 * card whose info trigger is hovered, so its tooltip paints above neighbors.
 *
 * The raise has to outlast the hover: the tooltip fades out over `slowTime`
 * (0.5s, `Components/ToolTip.elm`) after the pointer leaves, and dropping the
 * z-index the moment it does sank the fading card behind the next surface
 * card. So lowering waits out the fade (the delay on the resting state), and
 * raising is immediate (no delay while hovered). `allow-discrete` is what lets
 * `auto` take part in a transition at all. The resting value stays `auto`,
 * not `0`, so an unhovered card never becomes a stacking context that would
 * trap a tap-to-open popover inside it. */
[data-info-surface-card] {
  transition-property: z-index;
  transition-duration: 0s;
  transition-delay: 0.5s;
  transition-behavior: allow-discrete;
}

[data-info-surface-card]:has([data-tooltip="trigger"]:hover) {
  position: relative;
  z-index: 5;
  transition-delay: 0s;
}
/* ==================== END TOOLTIP CLAMP ==================== */

/* ==================== ICON GLOW ==================== */
/* A glow that follows the glyph instead of its box.
 *
 * `Border.shadow` draws its halo around the element's rounded rectangle, which
 * on a 24px icon reads as a solid coloured disc and spills over whatever the
 * icon sits beside. `drop-shadow` traces the rendered shape, so an arrow glows
 * arrow-shaped.
 *
 * `currentColor` takes the icon's own colour, so this one rule serves the green
 * links and the red clear button across every theme. Elements using it should
 * animate `filter` (see `transitionProperties`), since elm-ui writes its
 * transition inline and would otherwise win.
 *
 * `Style.Interaction.glyph` is the Elm side of this, and the reason focus is
 * here too: a keyboard user gets the same answer as a pointer.
 * `:focus-visible` rather than `:focus`, so clicking an icon
 * does not leave it glowing after the pointer has gone. */
[data-glow="icon"]:focus-visible {
  filter: drop-shadow(0 0 3px currentColor);
}

/* The same glow, asked for by the box around the glyph instead of by the
 * glyph itself.
 *
 * A navbar pill is one hit area with an icon and a word inside it. The pointer
 * is on the pill, so `[data-glow="icon"]:hover` on the glyphs would only fire
 * for the few pixels the glyph actually covers — and putting the marker on the
 * pill instead would trace the pill's own filled box, which is a slab of light
 * rather than a glowing word. So the pill wears `data-glow-within`
 * (`Style.Interaction.glowWithin`) and each glyph inside it keeps the marker
 * above (`Style.Interaction.glowTarget`).
 *
 * `:focus-visible` on the box for the same reason as above: a keyboard user
 * lands on the pill, not on the glyph. */
[data-glow-within]:focus-visible [data-glow="icon"] {
  filter: drop-shadow(0 0 3px currentColor);
}

/* A touch can leave :hover on the last tapped glyph. Hover light belongs to
 * pointers that can actually hover; keyboard light stays above. */
@media (hover: hover) {
  [data-glow="icon"]:hover,
  [data-glow-within]:hover [data-glow="icon"] {
    filter: drop-shadow(0 0 3px currentColor);
  }
}

/* OPTION ACTIONS — an action inside a combobox option, lit from the field's
 * → key (`Ui.Entry.Primitives.optionAction`, OPTION ACTIONS in `interop.ts`).
 * It never takes focus, which stays in the field, so it glows by attribute
 * as it would under the pointer. */
[data-option-action-active][data-glow="icon"],
[data-option-action-active] [data-glow="icon"] {
  filter: drop-shadow(0 0 3px currentColor);
}

/* THEME COLOURS IN CSS — every `var(--intent-…)` and `var(--secondary-var)`
 * below comes from `Style.Interaction.rootProperties`, a `:root` stylesheet
 * Elm mounts beside the rem bridge. An element picks a colour by naming it in
 * a `data-` attribute, never with an inline `--x` style: elm/virtual-dom
 * drops custom properties written through `Html.Attributes.style`, which is
 * why the rules here once read `--pill-accent`, `--info-hover-color` and
 * `--tracker-focus-*` and found nothing. */

/* PILL EMPHASIS — selectable Enaychi pills (`Ui/Pill.elm`). Their green
 * fill is the selected state and nothing else may look like it. Hover and
 * focus add only an accent border, a halo, and a text glow, and only where
 * they mean something: hover on devices that really hover, focus from the
 * keyboard. A tap leaves both `:hover` and `:focus` on the pill on a phone,
 * so with plain selectors a day just tapped off kept a green border. */
@media (hover: hover) {
  [data-pill-glow]:hover {
    border-color: var(--intent-go) !important;
    box-shadow: 0 0 12px var(--intent-go) !important;
  }

  [data-pill-glow]:hover [data-glow="icon"] {
    filter: drop-shadow(0 0 3px currentColor);
  }
}

[data-pill-glow]:focus-visible {
  border-color: var(--intent-go) !important;
  box-shadow: 0 0 12px var(--intent-go) !important;
}

[data-info-icon-trigger]:hover [data-glow="icon"] {
  color: var(--intent-go);
}

/* FOCUS HALO — keyboard focus on a filled box (`Style.Interaction.focusHalo`).
 *
 * The same 12px blur as `Interaction.haloShadow`, which is what the box shows on
 * hover, in the intent the element names. `:focus-visible` because a click
 * or tap also focuses the box, and `E.focused` (`:focus`) kept the halo on
 * after the pointer had gone. `!important` beats the `box-shadow` class
 * elm-ui writes for a box's own shadow. */
[data-hover-halo="brand"],
[data-focus-halo="brand"] {
  --focus-halo: var(--intent-brand);
}
[data-hover-halo="go"],
[data-focus-halo="go"] {
  --focus-halo: var(--intent-go);
}
[data-hover-halo="stop"],
[data-focus-halo="stop"] {
  --focus-halo: var(--intent-stop);
}
[data-hover-halo="quiet"],
[data-focus-halo="quiet"] {
  --focus-halo: var(--intent-quiet);
}
[data-focus-halo]:focus-visible {
  box-shadow: 0 0 var(--halo-blur) var(--focus-halo) !important;
}

/* Hover and focus share Interaction's colour and blur. Calendar fills are
 * supplied by hoverHaloIn; those cells have no standing shadow to preserve. */
@media (hover: hover) {
  [data-hover-halo]:hover {
    box-shadow: 0 0 var(--halo-blur) var(--focus-halo) !important;
  }
}
@media (hover: none) {
  [data-hover-halo-color]:hover {
    box-shadow: none !important;
  }
}

/* FOCUS RING — keyboard focus on a control that holds its halo at rest
 * (`Style.Interaction.focusRing`): the topbar day switcher, the dock bubbles.
 * `!important` because elm-ui's own `.s:focus { outline: none }` has the same
 * specificity and is written later, in the page body. */
[data-focus-ring]:focus-visible {
  outline: 2px solid currentColor !important;
  outline-offset: 3px;
}

/* FOCUS ROW — keyboard focus on a row in a list (`Style.Interaction.focusRow`).
 * Inside the row's edge, so it neither spills onto the neighbouring rows nor
 * is cut off by a scrolling list's clip. `!important` for the reason FOCUS
 * RING gives. */
[data-focus-row]:focus-visible {
  outline: 2px solid var(--intent-go) !important;
  outline-offset: -2px;
}

/* TARGET TABLE, LARGE TEXT (B-069) — `Ui.TargetTable`'s rows hold five
 * columns on one line, sized in rem so they grow with the reader's text. At a
 * large system font they no longer fit a phone: the bar shrank to nothing and
 * the score ran off the card. Below 19.5rem of viewport (rem here is the
 * WebView's default font size, which follows the system's) a row takes two
 * lines: the name and score, then the bar, % and amount. A 320px phone at the
 * default size stays on one line.
 */
@media (max-width: 19.5rem) {
  [data-target-row] {
    display: grid !important;
    grid-template-columns: minmax(0, 1fr) auto auto;
    grid-template-areas: "name name score" "bar dv amount";
    column-gap: 8px;
    row-gap: 2px;
    align-items: center;
  }
  [data-target-row] > * { margin: 0 !important; }
  [data-target-row] > [data-target-cell="name"] { grid-area: name; width: auto !important; min-width: 0 !important; max-width: none !important; }
  [data-target-row] > [data-target-cell="bar"] { grid-area: bar; width: auto !important; min-width: 0 !important; }
  [data-target-row] > [data-target-cell="dv"] { grid-area: dv; }
  [data-target-row] > [data-target-cell="amount"] { grid-area: amount; }
  [data-target-row] > [data-target-cell="score"] { grid-area: score; justify-self: end; }
}

/* ROVING ROWS — rows that are one tab stop between them (`Ui.TargetTable`;
 * the keys are ROVING ROWS in `interop.ts`). A row focused from the keyboard
 * shows the hover card a pointer gets (`Ui.Overlay.TargetTooltip`), which
 * elm-ui otherwise shows only on `:hover`. */
[data-roving-item]:focus-visible [data-tooltip="trigger"] {
  opacity: 1 !important;
}

/* FIELD FOCUS — a raw `<input>`/`<textarea>` takes `Interaction.fieldFocus`'s
 * border while the caret is in it (`Interaction.fieldFocusHtml`). `:focus` is
 * right for a field; see there. `!important` beats the inline border colour
 * the field frame writes. */
[data-focus-field]:focus {
  border-color: var(--intent-go) !important;
}
/* A borderless field (`Interaction.insetFieldFocusHtml`) rings inside its
 * edge instead, so the row around it keeps its size. */
[data-focus-field="inset"]:focus {
  box-shadow: inset 0 0 0 2px var(--intent-go) !important;
}

/* MENU FIELD FOCUS — a field that opens a menu takes the same border, from the
 * keyboard only (`Interaction.menuFieldFocus`). `Components.Dropdown.field`
 * wears the marker only while shut, so every picker drawn with that field
 * shows it the same way. `!important` beats the frame's border-colour class. */
[data-focus-menu-field]:focus-visible {
  border-color: var(--intent-go) !important;
}

/* Tracker segments use the same filled hover state for keyboard focus:
 * `Interaction.fill Go`. :focus-visible clears it after a mouse click when
 * the pointer leaves. */
[data-tracker-segment]:focus-visible {
  background-color: var(--intent-go);
  color: var(--intent-go-on);
  box-shadow: 0 0 12px var(--intent-go) !important;
}
/* ==================== END ICON GLOW ==================== */

/* ==================== ACCOUNT TOAST ==================== */
/* The two names deliberately have identical frames. Account alternates them
 * for queued messages, which restarts the animation when elm-ui retains the
 * same DOM node and merely changes its contents. */
@keyframes account-toast-even {
  0% {
    opacity: 1;
    transform: translateY(-28px);
  }
  12%,
  78% {
    opacity: 1;
    transform: translateY(0);
  }
  100% {
    opacity: 0;
    transform: translateY(-12px);
  }
}

@keyframes account-toast-odd {
  0% {
    opacity: 1;
    transform: translateY(-28px);
  }
  12%,
  78% {
    opacity: 1;
    transform: translateY(0);
  }
  100% {
    opacity: 0;
    transform: translateY(-12px);
  }
}
/* ==================== END ACCOUNT TOAST ==================== */

/* ==================== LOGO WATERMARK ==================== */
/* The mark behind a card, in one flat colour.
 *
 * The logo carries its own greys and orange as inline fills on every path, so
 * a colour set on the element it sits in cannot reach them. This overrides
 * them all with `currentColor`, which the wrapper then decides -- and
 * `!important` is what it takes, because an inline fill outranks any rule that
 * does not say so.
 *
 * Only inside a marked wrapper, so the logo keeps its real colours everywhere
 * else it is drawn. */
[data-logo="watermark"] path {
  fill: currentColor !important;
}
/* ==================== END LOGO WATERMARK ==================== */

/* ==================== CLIP X ==================== */
/* Hide horizontal overflow without becoming a scroll box.
 *
 * `overflow-x: hidden` alone computes `overflow-y` to `auto`, which turns the
 * element into a scroller of its own: anything that hangs below it, such as
 * an open dropdown menu, is cut off at its bottom edge instead of running on
 * into the page's real scroller. `clip` hides the same overflow and leaves
 * `overflow-y` visible. WebView 87–89 predate `clip` and keep `hidden`. */
[data-clip-x] {
  overflow-x: hidden;
  overflow-x: clip;
}
/* ==================== END CLIP X ==================== */

/* Immediate touch feedback for the phone day switcher. */
[data-day-bubble]:active { transform: scale(0.91); filter: brightness(1.15); }

/* ==================== LONG PRESS ==================== */
/* A held finger on a press-and-hold control (`Ui/LongPress.ts`) must not raise
   the touch callout or start a text selection. */
[data-long-press],
[data-hold-name] {
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
}

/* BUBBLE NAME: a held dock bubble's name (`Ui/LongPress.ts`), placed by
   script at the bubble's top centre and lifted clear of it. The surface and
   text are the theme's (`Style.Interaction.rootProperties`); the size is the
   `small` step of `Style.Fonts`. Above the dock (z-index 900). */
[data-bubble-name] {
  position: fixed;
  z-index: 950;
  transform: translate(-50%, calc(-100% - 10px));
  padding: 4px 10px;
  border-radius: 999px;
  background: var(--surface);
  color: var(--primary);
  box-shadow: 0 0 6px var(--intent-go);
  font-size: 0.8125rem;
  font-weight: 600;
  white-space: nowrap;
  pointer-events: none;
  animation: bubble-name-in 120ms ease-out;
}
@keyframes bubble-name-in {
  from { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  [data-bubble-name] { animation: none; }
}
/* ==================== END LONG PRESS ==================== */
