Ctrl K
ComunidadGuías…

frontend/src/app/globals.css

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
@tailwind base;
@tailwind components;
@tailwind utilities;

/* The app has no light theme, so the UA is told so once, at the root. Without
   this every browser-drawn control defaults to its light palette — most visibly
   the window and pane scrollbars, which rendered as light grey over the
   near-black surfaces. It also darkens native selects, date pickers and
   autofill, so it is the single declaration that keeps UA chrome on-theme.
   `scrollbar-color` is inherited, so setting it here reaches every scroll
   container that does not opt into the `::-webkit-scrollbar` styling below;
   it is honoured by Firefox and Chromium 121+, while older WebKit and Safari
   fall back to the dark UA scrollbar `color-scheme` already gives them.
   Thumb is `border.bright`, track is `surface` — the same tokens the panes use. */
:root {
  color-scheme: dark;
  scrollbar-width: thin;
  scrollbar-color: #3d3d5c #12121c;
}

html,
body {
  height: 100%;
}

body {
  @apply bg-background font-mono text-slate-200 antialiased;
}

::selection {
  @apply bg-primary text-black;
}

/* Thin scrollbars for log/stream panes. Defining any `::-webkit-scrollbar`
   pseudo-element makes Chromium ignore `scrollbar-color` for that element and
   draw a classic (non-overlay) bar, so the track is painted explicitly here
   rather than inherited. */
.scrollbar-thin::-webkit-scrollbar {
  width: 8px;
  height: 8px;
}
.scrollbar-thin::-webkit-scrollbar-track {
  @apply bg-transparent;
}
.scrollbar-thin::-webkit-scrollbar-thumb {
  @apply rounded-full bg-border-bright;
}
.scrollbar-thin::-webkit-scrollbar-corner {
  @apply bg-transparent;
}

/* Scrollable but chromeless — the landing nav's tab strip on narrow screens. */
.scrollbar-none {
  scrollbar-width: none;
}
.scrollbar-none::-webkit-scrollbar {
  display: none;
}

@layer utilities {
  /* Faint grid backdrop, tintable per module via `--grid-rgb` (inline style).
     Defaults to neutral slate; auth/landing set periwinkle, architect sets violet. */
  .bg-grid {
    background-image:
      linear-gradient(to right, rgb(var(--grid-rgb, 148 163 184) / 0.05) 1px, transparent 1px),
      linear-gradient(to bottom, rgb(var(--grid-rgb, 148 163 184) / 0.05) 1px, transparent 1px);
    background-size: 32px 32px;
  }

  /* Lit blueprint plane — the architect canvas backdrop. Three static layers on
     one element, split this way on purpose: `mask-image` applies to an element
     AND its pseudo-elements, so the grid's dissolve mask lives on ::before
     alone. Putting the vignette under that mask would erase it exactly at the
     edges where it has to be strongest. Hue via `--grid-rgb`, same contract as
     `.bg-grid`. Host element must be positioned. Nothing animates, so there is
     no reduced-motion entry. */
  .canvas-depth {
    pointer-events: none;
    background-image: radial-gradient(
      ellipse 90% 70% at 50% 34%,
      rgb(var(--grid-rgb, 148 163 184) / 0.05),
      transparent 70%
    );
  }
  .canvas-depth::before,
  .canvas-depth::after {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: inherit;
  }
  /* Two-tier grid: 128px majors over 32px minors. The 32px cadence and its 0.05
     alpha are shared with `.bg-grid` so the two backdrops stay one family; 128
     is 4x32, so every major lands on a minor and composites brighter. */
  .canvas-depth::before {
    background-image:
      linear-gradient(to right, rgb(var(--grid-rgb, 148 163 184) / 0.09) 1px, transparent 1px),
      linear-gradient(to bottom, rgb(var(--grid-rgb, 148 163 184) / 0.09) 1px, transparent 1px),
      linear-gradient(to right, rgb(var(--grid-rgb, 148 163 184) / 0.05) 1px, transparent 1px),
      linear-gradient(to bottom, rgb(var(--grid-rgb, 148 163 184) / 0.05) 1px, transparent 1px);
    background-size:
      128px 128px,
      128px 128px,
      32px 32px,
      32px 32px;
    -webkit-mask-image: radial-gradient(ellipse 88% 78% at 50% 40%, #000 45%, transparent 100%);
    mask-image: radial-gradient(ellipse 88% 78% at 50% 40%, #000 45%, transparent 100%);
  }
  /* Edge vignette — deliberately outside the mask above. Darkens toward the
     page `background` token, not a new hue. */
  .canvas-depth::after {
    background: radial-gradient(
      ellipse 80% 80% at 50% 45%,
      transparent 45%,
      rgb(10 10 16 / 0.5) 100%
    );
  }

  /* Colored spotlight beam for the landing stage. A beam is three stacked
     parts — source hotspot, coned shaft, hot core — each hued via its inline
     gradient. The shaft and core get a slow opacity breathe plus a gentle sway
     around the top apex; the source only breathes (swaying it would drift the
     lamp off the apex). Each part carries its own duration/delay so beams never
     move in lockstep. Breathe touches only opacity and sway only transform, so
     they never fight over the same property on one element. */
  .spotlight-beam,
  .spotlight-core {
    animation:
      spotlight-breathe var(--beam-dur, 8s) ease-in-out infinite,
      spotlight-sway var(--beam-sway, 16s) ease-in-out infinite;
  }
  .spotlight-source {
    animation: spotlight-breathe var(--beam-dur, 8s) ease-in-out infinite;
  }

  /* Uppercase micro-label used for stat labels and section eyebrows. */
  .text-micro {
    @apply text-[10px] font-semibold uppercase tracking-micro;
  }

  /* Vertical label for a collapsed side rail — reads bottom-to-top. The rotate
     is what flips `vertical-rl` from top-down to bottom-up, which is the
     conventional direction for a Latin tab label. Compose with `text-micro`. */
  .text-vertical {
    writing-mode: vertical-rl;
    transform: rotate(180deg);
  }

  /* Architect step-2 column resize when a side panel collapses to its rail.
     A tween, not a spring: FlowLayer re-measures the whole agent graph on every
     frame of this transition, so overshoot would cost real work. */
  .panel-grid {
    transition: grid-template-columns 250ms cubic-bezier(0.16, 1, 0.3, 1);
  }

  /* Animated neon gradient border for featured cards — double-background
     technique, hue via `--gb-rgb` (inline style). Degrades to a static
     gradient border without animation support. */
  .gradient-border {
    border: 1px solid transparent;
    background:
      linear-gradient(#12121c, #12121c) padding-box,
      linear-gradient(
          90deg,
          rgb(var(--gb-rgb, 211 203 192) / 0.7),
          rgb(var(--gb-rgb, 211 203 192) / 0.1) 40%,
          rgb(var(--gb-rgb, 211 203 192) / 0.7)
        )
        border-box;
    background-size:
      100% 100%,
      200% 100%;
    animation: border-pan 6s linear infinite;
  }

  /* Diagonal light sweep on hover — for solid CTAs and featured cards. */
  .shimmer-hover {
    position: relative;
    overflow: hidden;
  }
  .shimmer-hover::after {
    content: '';
    position: absolute;
    inset: 0;
    width: 40%;
    transform: translateX(-150%) skewX(-12deg);
    background: linear-gradient(110deg, transparent, rgb(255 255 255 / 0.14), transparent);
    pointer-events: none;
  }
  .shimmer-hover:hover::after {
    animation: shimmer 0.9s ease-out;
  }

  /* 1px neon line sweeping down once on route entry — tint via `--pt-rgb`.
     Fixed so it crosses the whole viewport regardless of scroll position. */
  .page-scanline {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: 1px;
    z-index: 60;
    pointer-events: none;
    background: linear-gradient(
      90deg,
      transparent,
      rgb(var(--pt-rgb, 211 203 192) / 0.9) 30%,
      rgb(var(--pt-rgb, 211 203 192) / 0.9) 70%,
      transparent
    );
    box-shadow: 0 0 12px 1px rgb(var(--pt-rgb, 211 203 192) / 0.55);
    animation: scanline 0.35s ease-in both;
  }

  /* Sparkline entrance — bars grow from the baseline, staggered per bar via
     inline animation-delay. */
  .spark-grow rect {
    transform-box: fill-box;
    transform-origin: bottom;
    animation: spark-grow 0.5s ease-out both;
  }

  /* Staggered blur-in for a container's direct children (forms, card innards).
     Caps at 10 children; later ones share the last beat. */
  .stagger-children > * {
    animation: stagger-in 0.45s cubic-bezier(0.16, 1, 0.3, 1) both;
  }
  .stagger-children > *:nth-child(2) {
    animation-delay: 0.06s;
  }
  .stagger-children > *:nth-child(3) {
    animation-delay: 0.12s;
  }
  .stagger-children > *:nth-child(4) {
    animation-delay: 0.18s;
  }
  .stagger-children > *:nth-child(5) {
    animation-delay: 0.24s;
  }
  .stagger-children > *:nth-child(6) {
    animation-delay: 0.3s;
  }
  .stagger-children > *:nth-child(7) {
    animation-delay: 0.36s;
  }
  .stagger-children > *:nth-child(n + 8) {
    animation-delay: 0.42s;
  }

  /* One-shot light pan along the TopBar bottom edge — tint via `--tb-rgb`. */
  .topbar-pan {
    background: linear-gradient(
      90deg,
      transparent,
      rgb(var(--tb-rgb, 211 203 192) / 0.8),
      transparent
    );
    background-size: 40% 100%;
    background-repeat: no-repeat;
    animation: topbar-pan 0.7s ease-out both;
  }

  /* Horizontal bar entrance (BarList) — pair with inline animation-delay. */
  .bar-grow {
    transform-origin: left;
    animation: bar-grow 0.6s cubic-bezier(0.16, 1, 0.3, 1) both;
  }

  /* Power-on line sweeping down a panel once per task start — `--ps-rgb`. */
  .panel-scan {
    position: absolute;
    inset: 0;
    pointer-events: none;
    background-image: linear-gradient(
      90deg,
      transparent,
      rgb(var(--ps-rgb, 167 139 250) / 0.8),
      transparent
    );
    background-size: 100% 2px;
    background-repeat: no-repeat;
    animation: panel-scan 0.6s ease-in both;
  }

  /* Architect task rail — one status bar per task, hue via `--bar-rgb`. The
     glow is what makes a 6px bar read as neon rather than as a plain divider. */
  .rail-bar {
    background-color: rgb(var(--bar-rgb, 139 143 163));
    box-shadow: 0 0 8px 0 rgb(var(--bar-rgb, 139 143 163) / 0.45);
  }

  /* Selected task: `--glow-rgb` (the architect hue) blooms around the bar,
     which keeps its own status color. */
  .rail-bar-active {
    box-shadow:
      0 0 10px 0 rgb(var(--bar-rgb, 139 143 163) / 0.6),
      0 0 20px 4px rgb(var(--glow-rgb, 157 77 255) / 0.5);
  }

  /* Fresh EventLog row: tinted highlight decaying to transparent — `--lg-rgb`. */
  .log-flash {
    animation: log-flash 1.2s ease-out both;
  }

  /* Loading placeholder sheen — module tint via `--sk-rgb`. */
  .skeleton {
    @apply rounded-md bg-surface-2;
    background-image: linear-gradient(
      100deg,
      transparent 30%,
      rgb(var(--sk-rgb, 148 163 184) / 0.08) 50%,
      transparent 70%
    );
    background-size: 200% 100%;
    background-repeat: no-repeat;
    animation: skeleton-sheen 1.6s ease-in-out infinite;
  }

  /* Toast entrance — a short fade + slide from the right edge. */
  .toast-in {
    animation: toast-in 0.22s ease-out both;
  }

  /* Sponsor like-burst — a particle flies out along its own `--tx`/`--ty`. */
  .like-particle {
    animation: like-particle-fly 0.5s cubic-bezier(0.16, 1, 0.3, 1) both;
  }
  /* Sponsor like-burst — the filled heart pops, then fades to the outline. */
  .like-heart-pop {
    animation: like-heart-pop 0.45s cubic-bezier(0.16, 1, 0.3, 1) both;
  }

  @media (prefers-reduced-motion: reduce) {
    .gradient-border,
    .shimmer-hover:hover::after,
    .skeleton,
    .spark-grow rect,
    .stagger-children > *,
    .topbar-pan,
    .bar-grow,
    .log-flash,
    .toast-in,
    .like-particle,
    .like-heart-pop {
      animation: none;
    }
    .spotlight-beam,
    .spotlight-source,
    .spotlight-core {
      animation: none;
    }
    .page-scanline,
    .panel-scan {
      display: none;
    }
    .panel-grid {
      transition: none;
    }
  }
}

@keyframes spotlight-breathe {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.55;
  }
}

@keyframes spotlight-sway {
  0%,
  100% {
    transform: rotate(-1.5deg);
  }
  50% {
    transform: rotate(1.5deg);
  }
}

@keyframes spark-grow {
  from {
    transform: scaleY(0);
  }
}

@keyframes toast-in {
  from {
    opacity: 0;
    transform: translateX(12px);
  }
}

@keyframes like-particle-fly {
  from {
    transform: translate(-50%, -50%) scale(0.2);
    opacity: 1;
  }
  60% {
    opacity: 1;
  }
  to {
    transform: translate(calc(-50% + var(--tx)), calc(-50% + var(--ty))) scale(0.5);
    opacity: 0;
  }
}

@keyframes like-heart-pop {
  0% {
    transform: scale(0.6);
    opacity: 0;
  }
  40% {
    transform: scale(1.35);
    opacity: 1;
  }
  70% {
    transform: scale(0.95);
    opacity: 1;
  }
  100% {
    transform: scale(1);
    opacity: 0;
  }
}

@keyframes stagger-in {
  from {
    opacity: 0;
    transform: translateY(8px);
    filter: blur(4px);
  }
}

@keyframes topbar-pan {
  from {
    background-position: -40% 0;
  }
  to {
    background-position: 140% 0;
  }
}

@keyframes bar-grow {
  from {
    transform: scaleX(0);
  }
}

@keyframes skeleton-sheen {
  from {
    background-position: -150% 0;
  }
  to {
    background-position: 250% 0;
  }
}

@keyframes scanline {
  from {
    transform: translateY(0);
    opacity: 0.9;
  }
  to {
    transform: translateY(100vh);
    opacity: 0;
  }
}

@keyframes panel-scan {
  from {
    background-position: 0 0;
    opacity: 1;
  }
  to {
    background-position: 0 100%;
    opacity: 0;
  }
}

@keyframes log-flash {
  0% {
    background-color: rgb(var(--lg-rgb, 167 139 250) / 0.14);
    opacity: 0;
    transform: translateY(6px);
  }
  25% {
    opacity: 1;
    transform: translateY(0);
  }
  100% {
    background-color: transparent;
    opacity: 1;
    transform: translateY(0);
  }
}

/* ---- Per-domain squad-card hover motifs ---------------------------------- */
/* Decorative loop timings (~2s) sit outside motion.ts's DUR scale (one-shots,
   ≤0.8s), so they are CSS literals here — consistent with the keyframes above.
   The shared EASE.out curve is reused as the cubic-bezier literal. Every motif
   applies to nodes tagged [data-play], which are paused until the card hovers.

   These rules are intentionally unlayered: the `.motif-*` classes below use the
   `animation` shorthand, which resets `animation-play-state` to `running`. An
   unlayered rule beats Tailwind's layered utilities, and the higher-specificity
   `.card-motif [data-play]` selector re-asserts `paused` over that shorthand, so
   the grid stays idle until a card is hovered. */

/* Rendered always, invisible + paused at rest; fades to a faint watermark and
   resumes only while the parent card (a `.group`) is hovered. */
.card-motif {
  opacity: 0;
  transition: opacity 0.3s ease-out;
}
.group:hover .card-motif {
  opacity: 0.16;
}
.card-motif [data-play] {
  transform-box: fill-box; /* transform-origin relative to each node's own box */
  animation-play-state: paused;
}
.group:hover .card-motif [data-play] {
  animation-play-state: running;
}
/* Rotations/tilts that must pivot on a point in the SVG, not each node's box. */
.card-motif .motif-spin {
  transform-box: view-box;
  transform-origin: 50% 50%;
}
.card-motif .motif-tip {
  transform-box: view-box;
  transform-origin: 50% 28%;
}
.card-motif .motif-swing {
  transform-box: view-box;
  transform-origin: 75% 41%; /* the tassel's attachment point on the cap */
}
@media (prefers-reduced-motion: reduce) {
  /* Freeze at the static frame; the hover opacity fade (a transition) stays. */
  .card-motif [data-play] {
    animation: none;
  }
}

/* Software: each code line wipes in from the left, holds, then retypes. */
.motif-codeline {
  animation: motif-codeline 2.4s cubic-bezier(0.16, 1, 0.3, 1) infinite;
}
@keyframes motif-codeline {
  0% {
    transform: scaleX(0);
    opacity: 0;
  }
  20% {
    opacity: 1;
  }
  40%,
  80% {
    transform: scaleX(1);
    opacity: 1;
  }
  100% {
    transform: scaleX(0);
    opacity: 0;
  }
}

/* Blinking text caret. */
.motif-caret {
  animation: motif-caret 1s steps(1) infinite;
}
@keyframes motif-caret {
  0%,
  49% {
    opacity: 1;
  }
  50%,
  100% {
    opacity: 0;
  }
}

/* Finance: a candle grows from the baseline, holds, then the oldest fades out. */
.motif-candle {
  animation: motif-candle 2s cubic-bezier(0.16, 1, 0.3, 1) infinite;
}
@keyframes motif-candle {
  0% {
    transform: scaleY(0);
    opacity: 0;
  }
  25%,
  80% {
    transform: scaleY(1);
    opacity: 1;
  }
  100% {
    transform: scaleY(1);
    opacity: 0;
  }
}

/* Expanding signal ring / ripple (marketing rings, searching + opensource + local). */
.motif-ping {
  animation: motif-ping 1.8s ease-out infinite;
  transform-origin: center;
}
@keyframes motif-ping {
  0% {
    transform: scale(0.3);
    opacity: 0.9;
  }
  100% {
    transform: scale(1.9);
    opacity: 0;
  }
}

/* SEO: the magnifier sweeps back and forth across the result rows. */
.motif-scan {
  animation: motif-scan 2.4s ease-in-out infinite;
}
@keyframes motif-scan {
  0%,
  100% {
    transform: translateX(-8px);
  }
  50% {
    transform: translateX(6px);
  }
}

/* SEO: the rank arrow ticks up and fades. */
.motif-rankup {
  animation: motif-rankup 2.4s ease-in-out infinite;
}
@keyframes motif-rankup {
  0%,
  100% {
    transform: translateY(4px);
    opacity: 0;
  }
  50% {
    transform: translateY(-2px);
    opacity: 1;
  }
}

/* Continuous rotation (radar sweep, electron orbit). */
.motif-spin {
  animation: motif-spin 3s linear infinite;
}
@keyframes motif-spin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

/* Data: a bar rises and falls, staggered into a travelling wave. */
.motif-bar {
  animation: motif-bar 1.6s ease-in-out infinite;
}
@keyframes motif-bar {
  0%,
  100% {
    transform: scaleY(0.25);
  }
  50% {
    transform: scaleY(1);
  }
}

/* Stroke that draws itself along a normalised path (content frame, git branch). */
.motif-draw {
  stroke-dasharray: 1;
  animation: motif-draw 2.6s ease-in-out infinite;
}
@keyframes motif-draw {
  0% {
    stroke-dashoffset: 1;
  }
  60%,
  100% {
    stroke-dashoffset: 0;
  }
}

/* Legal: the balance beam tips gently back and forth. */
.motif-tip {
  animation: motif-tip 2.6s ease-in-out infinite;
}
@keyframes motif-tip {
  0%,
  100% {
    transform: rotate(-9deg);
  }
  50% {
    transform: rotate(9deg);
  }
}

/* Education: the tassel swings like a pendulum (pivot set in the gating block). */
.motif-swing {
  animation: motif-swing 2.2s ease-in-out infinite;
}
@keyframes motif-swing {
  0%,
  100% {
    transform: rotate(-16deg);
  }
  50% {
    transform: rotate(16deg);
  }
}

/* Chat/network dot pulse (social typing dots, community nodes). */
.motif-typedot {
  animation: motif-typedot 1.4s ease-in-out infinite;
}
@keyframes motif-typedot {
  0%,
  70%,
  100% {
    transform: scale(0.7);
    opacity: 0.4;
  }
  35% {
    transform: scale(1.15);
    opacity: 1;
  }
}

/* Community: a connective link brightens and dims. */
.motif-link {
  animation: motif-link 2s ease-in-out infinite;
}
@keyframes motif-link {
  0%,
  100% {
    opacity: 0.25;
  }
  50% {
    opacity: 1;
  }
}

/* Local: the pin drops in and settles with a short bounce. */
.motif-drop {
  animation: motif-drop 2.4s ease-in-out infinite;
}
@keyframes motif-drop {
  0% {
    transform: translateY(-16px);
    opacity: 0;
  }
  25% {
    opacity: 1;
  }
  45% {
    transform: translateY(0);
  }
  58% {
    transform: translateY(-4px);
  }
  70%,
  100% {
    transform: translateY(0);
    opacity: 1;
  }
}

/* General: sparkles twinkle in and out of phase. */
.motif-twinkle {
  animation: motif-twinkle 1.8s ease-in-out infinite;
}
@keyframes motif-twinkle {
  0%,
  100% {
    transform: scale(0.4);
    opacity: 0.2;
  }
  50% {
    transform: scale(1);
    opacity: 1;
  }
}