/* SPDX-License-Identifier: Apache-2.0 */
/* Brand styling layer, ported from the OpenRun management console
   (ui/console/static/css/style.css + the accent-text token from
   accessibility.css). Theme colors come from the daisyui custom themes in
   app.star; everything here uses theme variables so it adapts to
   light/dark. */

/* ---- Typography ---------------------------------------------------------
   Self-hosted brand type (SIL OFL, woff2 in static/fonts/): Space Grotesk
   for headings, Inter for body, JetBrains Mono for identifiers (paths,
   shas, specs, sources). The generated tailwind layer routes every font
   through --font-sans/--font-mono, so redefining the variables here covers
   the preflight body default and the font-sans/font-mono utilities alike.
   Shipped weights only: Inter 400/500/600, Space Grotesk 500/700,
   JetBrains Mono 400/500. */
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/inter-400.woff2") format("woff2");
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../fonts/inter-500.woff2") format("woff2");
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../fonts/inter-600.woff2") format("woff2");
}
@font-face {
  font-family: "Space Grotesk";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../fonts/space-grotesk-500.woff2") format("woff2");
}
@font-face {
  font-family: "Space Grotesk";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../fonts/space-grotesk-700.woff2") format("woff2");
}
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/jetbrains-mono-400.woff2") format("woff2");
}
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../fonts/jetbrains-mono-500.woff2") format("woff2");
}

:root {
  --ease-out-soft: cubic-bezier(0.22, 1, 0.36, 1);
  --font-sans: "Inter", ui-sans-serif, system-ui, sans-serif,
    "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol",
    "Noto Color Emoji";
  --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Monaco,
    Consolas, "Liberation Mono", "Courier New", monospace;
}

/* Headings carry the display face. Unlayered rule (beats layered tailwind
   utilities), so mono headings must be excluded explicitly */
h1:not(.font-mono),
h2:not(.font-mono),
h3:not(.font-mono),
.page-title {
  font-family: "Space Grotesk", var(--font-sans);
}

/* ---- AA-safe brand green text ------------------------------------------
   The brand light green (--color-primary #00c200) is a FILL color: as text
   on light surfaces it measures ~2.3:1. --or-accent-text is the readable
   brand green for text in the active theme: dark green on light surfaces,
   bright green on dark. */
:root {
  --or-accent-text: #007700; /* light default, 5.7:1 on white */
}
:root[data-theme="openrun-dark"] {
  --or-accent-text: var(--color-primary); /* 6.8:1 on the dark card */
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="openrun-light"]) {
    --or-accent-text: var(--color-primary);
  }
}
.text-primary,
.link-primary {
  color: var(--or-accent-text);
}
.badge-soft.badge-primary {
  color: var(--or-accent-text);
}

/* ---- Page title ---------------------------------------------------------
   The heading carries the brand accent fade bar, matching the console
   screens. Width tracks the "OpenRun" prefix (em-based). */
.page-title::after {
  content: "";
  display: block;
  width: 4.3em;
  height: 3px;
  margin-top: 0.3rem;
  margin-bottom: 0.6rem;
  border-radius: 2px;
  background: linear-gradient(
    90deg,
    color-mix(in oklch, var(--color-secondary) 5%, transparent),
    var(--color-secondary)
  );
}

/* ---- Branded loading indicator -----------------------------------------
   A thin primary-colored progress line instead of a spinner. The element
   carries .htmx-indicator, so htmx fades it in during requests. */
.progress-line {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 2px;
  overflow: hidden;
  pointer-events: none;
  transition: opacity 200ms ease;
}
.progress-line::after {
  content: "";
  position: absolute;
  inset: 0;
  width: 40%;
  border-radius: 2px;
  background: var(--color-primary);
  animation: progress-sweep 1.1s var(--ease-out-soft) infinite;
}
@keyframes progress-sweep {
  from {
    transform: translateX(-110%);
  }
  to {
    transform: translateX(360%);
  }
}

/* ---- Cards --------------------------------------------------------------
   Content cards lift very slightly on hover, matching the console. */
.card.border {
  transition:
    box-shadow 200ms var(--ease-out-soft),
    border-color 200ms var(--ease-out-soft);
}
.card.border:hover {
  box-shadow: 0 2px 10px -4px
    color-mix(in oklab, var(--color-secondary) 24%, transparent);
  border-color: color-mix(in oklab, var(--color-secondary) 24%, var(--color-base-300));
}

/* Brand hairline riding each card's top edge, covering half the card
   width; brightens on hover for pop (the console's .ov-tile::after) */
.card.border {
  position: relative;
}
.card.border::after {
  content: "";
  position: absolute;
  top: -1px;
  left: 0.9rem;
  width: 50%;
  height: 2px;
  border-radius: 2px;
  background: linear-gradient(
    90deg,
    var(--color-secondary),
    color-mix(in oklch, var(--color-secondary) 5%, transparent)
  );
  opacity: 0.55;
  transition: opacity 150ms ease;
}
.card.border:hover::after {
  opacity: 1;
}

/* ---- Field focus --------------------------------------------------------
   Focused input glows softly in the brand color. */
input.input {
  transition:
    border-color 200ms ease,
    box-shadow 200ms ease;
}
input.input:focus-within {
  border-color: var(--color-primary);
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--color-primary) 15%, transparent);
}

/* Visible keyboard focus everywhere, in the brand color */
:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

/* Skip link: visually hidden until it receives keyboard focus. Main
   content comes first in DOM order, so the link jumps forward to the
   folder navigation. */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0.5rem;
  z-index: 200;
}
.skip-link:focus-visible {
  left: 0.5rem;
  padding: 0.4rem 0.8rem;
  border-radius: var(--radius-field);
  background: var(--color-primary);
  color: var(--color-primary-content);
  font-size: 0.85rem;
}

/* The theme toggle checkbox stays in the tab order. daisyui's swap hides
   the input (visibility: hidden), which also removes it from keyboard
   focus; keep it visible but transparent, stretched over the button so
   its focus ring wraps the control */
.swap input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  visibility: visible;
  cursor: pointer;
}

/* ---- HTMX swap polish ---------------------------------------------------
   Swapped-in results settle with a soft fade/slide. */
@keyframes fade-slide-in {
  from {
    opacity: 0.4;
    transform: translateY(3px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}
.htmx-settling {
  animation: fade-slide-in 250ms var(--ease-out-soft);
}

/* ---- Folder tree sidebar ------------------------------------------------
   Console-style tree rows: sans/mono labels, count pills, solid primary
   active row. Colors ride the theme variables for light/dark. */
.tree-heading {
  font-size: 0.6875rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--or-accent-text);
}
.tree-row {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.4rem 0.5rem;
  border-radius: 0.5rem;
  color: var(--color-base-content);
  transition: background-color 150ms ease;
}
.tree-row:hover {
  background: color-mix(in oklab, var(--color-base-content) 6%, transparent);
}
/* Selected row is SOLID primary (the console active-chip convention) */
.tree-active,
.tree-active:hover {
  background: var(--color-primary);
  color: var(--color-primary-content);
  font-weight: 600;
}
.tree-active .tree-icon,
.tree-active .tree-caret {
  color: currentColor;
}
.tree-active .tree-count {
  background: color-mix(in oklab, var(--color-primary-content) 14%, transparent);
  color: currentColor;
}
.tree-count {
  margin-left: auto;
  font-size: 0.6875rem;
  /* 70% floor: small text below that fails AA on the tinted pill */
  color: color-mix(in oklab, var(--color-base-content) 72%, transparent);
  background: color-mix(in oklab, var(--color-base-content) 7%, transparent);
  border-radius: 99px;
  padding: 0 0.45rem;
  flex: none;
}
.tree-caret {
  width: 13px;
  height: 13px;
  flex: none;
  color: color-mix(in oklab, var(--color-base-content) 45%, transparent);
  transition: transform 150ms ease;
}
.tree-caret.open {
  transform: rotate(90deg);
}
.tree-icon {
  flex: none;
  color: var(--or-accent-text);
}

/* ---- Resizable sidebar (md+), ported from the console -------------------
   Native CSS resize (no script): the browser renders a corner grip at the
   sidebar's bottom-right. Reserve the docked sidebar column up front for
   load stability, then let the column track the side's width so resizing
   hands freed space to the content. Sizes are not persisted across
   navigations - inherent to the CSS-only approach. */
@media (min-width: 48rem) {
  .drawer.md\:drawer-open {
    grid-template-columns: 16rem auto;
    background: linear-gradient(
      to right,
      var(--color-base-100) calc(16rem - 1px),
      var(--color-base-300) calc(16rem - 1px),
      var(--color-base-300) 16rem,
      transparent 16rem
    );
  }
  .drawer.md\:drawer-open:has(.drawer-side) {
    grid-template-columns: max-content auto;
    background: none;
  }
  /* The resize must live on .drawer-side: the grid column tracks the
     side's definite width, and with overflow hidden the grid item's
     automatic min-size floor is dropped so the column actually follows */
  .drawer-side {
    width: 16rem; /* starting size; the browser resize writes inline width */
    resize: horizontal;
    overflow: hidden;
    min-width: 11rem;
    max-width: 50rem;
  }
  /* The aside is a fixed column (logo / scrolling tree / theme footer);
     the tree region scrolls internally, so the aside itself must not */
  .drawer-side > aside {
    width: 100%;
    min-width: 0;
    overflow: hidden;
  }
}

/* Keep the wide sidebar brand legible without crowding the drawer */
.sidebar-logo {
  height: 3.75rem;
}
@media (min-width: 48rem) {
  .sidebar-logo {
    height: 5rem;
  }
}

/* A small moment of delight on the logo, as in the console */
.logo-mark {
  transition: transform 250ms var(--ease-out-soft);
}
.logo-mark:hover {
  transform: scale(1.05);
}

/* ---- Tooltips -----------------------------------------------------------
   Cap bubble width so long app paths in data-tip stay readable; narrower
   on small screens where a wide bubble would run off the viewport. */
.tooltip:before {
  max-width: 18rem;
}
@media (max-width: 48rem) {
  .tooltip:before {
    max-width: 10rem;
  }
}

/* Tooltip bubbles cannot escape .drawer-side's overflow:hidden (set for
   the native resize), so a wide bubble clips at the sidebar edge. Cap
   sidebar bubbles so they wrap and fit inside (console sidebar
   convention) */
.drawer-side .tooltip:before {
  max-width: 9rem;
}

/* Hidden tooltip bubbles must not widen the page. daisyui keeps the
   bubble in layout (position:absolute, opacity:0), so a bubble near the
   right viewport edge adds horizontal page scroll on mobile even though
   nothing is visible. While hidden, park it as position:fixed - fixed
   boxes never contribute to scrollable overflow - and let the show
   states (mirroring daisyui's) restore the normal absolute placement.
   The opacity transitions are unaffected. */
.tooltip:not(:hover):not(.tooltip-open):not(:has(:focus-visible)) > .tooltip-content,
.tooltip[data-tip]:not(:hover):not(.tooltip-open):not(:has(:focus-visible))::before,
.tooltip:not(:hover):not(.tooltip-open):not(:has(:focus-visible))::after {
  position: fixed;
}

/* ---- Accessibility ------------------------------------------------------
   All motion is removed for users who prefer reduced motion. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}
