/* TorBula · Herramientas — Apple "Liquid Glass" redesign
   Self-contained. Mirrors MiniJuegos/ (same tokens, glass material and
   macOS-window cards) but lists the standalone tool sub-apps instead of
   games. Theme is shared with the portfolio via localStorage.theme + the
   `dark` class on <html>. */

/* ═══════════════════════════════════════════════
   TOKENS  (kept in sync with the root index.css)
═══════════════════════════════════════════════ */
:root {
  --bg:        #fbfbfd;
  --surface:   #ffffff;
  --t1:        #1d1d1f;
  --t2:        #86868b;
  --t3:        #aeaeb2;
  --blue:      #0071e3;
  --hair:      rgba(0,0,0,.10);

  --glass-tint:
    linear-gradient(180deg, rgba(255,255,255,.58) 0%, rgba(255,255,255,.20) 46%, rgba(255,255,255,.34) 100%);
  --glass-edge:
    inset 0 1px 0.5px rgba(255,255,255,.95),
    inset 1px 0 0.5px rgba(255,255,255,.5),
    inset -1px 0 0.5px rgba(255,255,255,.4),
    inset 0 -1px 1px rgba(255,255,255,.28),
    inset 0 0 12px rgba(255,255,255,.14);
  --glass-border: rgba(255,255,255,.55);
  --glass-drop:
    0 1px 1px rgba(0,0,0,.04),
    0 6px 16px rgba(0,0,0,.06),
    0 18px 50px rgba(0,0,0,.08);

  --font: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'SF Pro Text',
          'Helvetica Neue', system-ui, sans-serif;

  --spring:  cubic-bezier(.34, 1.46, .5, 1);
  --spring2: cubic-bezier(.16, 1, .3, 1);
  --ease:    cubic-bezier(.4, 0, .2, 1);

  --topbar-h: 54px;

  --cur-arrow: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='18'%20height='24'%20viewBox='0%200%2018%2024'%3E%3Cpath%20d='M1.5%201%20L1.5%2017.9%20L5.8%2013.6%20L8.5%2019.9%20L10.9%2018.8%20L8.3%2012.7%20L14.1%2012.7%20Z'%20fill='%23000'%20stroke='%23fff'%20stroke-width='1.5'%20stroke-linejoin='round'/%3E%3C/svg%3E") 2 1, auto;
}

html.dark {
  --bg:       #000000;
  --surface:  #1c1c1e;
  --t1:       #f5f5f7;
  --t2:       #98989d;
  --t3:       #636366;
  --blue:     #0a84ff;
  --hair:     rgba(255,255,255,.12);
  --glass-tint:
    linear-gradient(180deg, rgba(70,70,76,.55) 0%, rgba(40,40,46,.28) 50%, rgba(60,60,66,.40) 100%);
  --glass-edge:
    inset 0 1px 0.5px rgba(255,255,255,.32),
    inset 1px 0 0.5px rgba(255,255,255,.14),
    inset -1px 0 0.5px rgba(255,255,255,.12),
    inset 0 -1px 1px rgba(0,0,0,.3),
    inset 0 0 14px rgba(255,255,255,.05);
  --glass-border: rgba(255,255,255,.16);
  --glass-drop:
    0 1px 1px rgba(0,0,0,.3),
    0 8px 24px rgba(0,0,0,.5),
    0 24px 60px rgba(0,0,0,.55);
}

/* ═══════════════════════════════════════════════
   RESET
═══════════════════════════════════════════════ */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html {
  -webkit-text-size-adjust: 100%; cursor: var(--cur-arrow);
  overflow-x: hidden;
}
body {
  font-family: var(--font);
  background: var(--bg);
  color: var(--t1);
  min-height: 100vh;
  line-height: 1.5;
  letter-spacing: -.01em;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  transition: color .5s var(--ease);
}
a { color: inherit; text-decoration: none; cursor: var(--cur-arrow); }
button { font-family: inherit; border: none; background: none; cursor: var(--cur-arrow); color: inherit; }
img, svg { display: block; }

/* ── Custom overlay scrollbar (DESKTOP ONLY — on touch the native overlay bar
      stays) — the native viewport bar is hidden and replaced by #cscroll:
      a fixed element that FLOATS OVER the content, so
      there's no reserved gutter, nothing shifts, and the luminous background
      runs edge-to-edge. Thumb-only, macOS/iOS style: fades in on scroll or on
      hovering the right edge, fades out when idle, widens on hover, draggable;
      clicking the empty track jumps there. Geometry + behavior live in
      index.js (CUSTOM SCROLLBAR). NEUTRAL grey so it reads on BOTH themes. ── */
@media (hover: hover) {   /* desktop only — on touch the native overlay bar stays */
  html { scrollbar-width: none; }                          /* Firefox + Chromium 121+ */
  html::-webkit-scrollbar,
  body::-webkit-scrollbar { display: none; width: 0; height: 0; }   /* Safari / older Chromium */
}
.cscroll {
  position: fixed; top: 4px; right: 3px; bottom: 4px; width: 14px;
  z-index: 900;              /* above the topbar (800); below the tool overlay (9999) */
  opacity: 0;
  transition: opacity .4s var(--ease);
}
.cscroll.on { opacity: 1; }
.cscroll-thumb {
  position: absolute; top: 0; right: 0; width: 6px;
  border-radius: 99px;
  background: rgba(128,128,136,.55);
  transition: width .18s var(--ease), background-color .18s var(--ease);
  will-change: transform;
  touch-action: none;   /* required for pointer-drag on touch — else the browser
                           turns the gesture into a page scroll and fires pointercancel */
  -webkit-tap-highlight-color: transparent;
}
@media (hover: hover) {
  .cscroll:hover .cscroll-thumb,
  .cscroll.drag  .cscroll-thumb { width: 10px; background: rgba(128,128,136,.85); }
}
@media (hover: none) {   /* touch: mobile browsers already overlay the native bar over
                            the content (no gutter, nothing shifts), so keep the native
                            indicator there and drop the custom bar entirely */
  .cscroll { display: none; }
}

/* ═══════════════════════════════════════════════
   LUMINOUS BACKGROUND
═══════════════════════════════════════════════ */
.bg-field {
  position: fixed; top: 0; left: 0; z-index: -2;
  width: 100vw; height: 100vh;   /* no native scrollbar gutter anymore — the custom
                                    bar overlays, so the bg runs edge-to-edge */
  background: var(--bg); overflow: hidden;
  transition: background .5s var(--ease);
}
.orb {
  position: absolute;
  width: 46vw; height: 46vw;
  min-width: 380px; min-height: 380px;
  border-radius: 50%;
  filter: blur(90px); opacity: .5;
  will-change: transform;
}
.orb-1 { background: #cfe1ff; top: -12vw; left: -8vw;  animation: drift1 26s var(--ease) infinite; }
.orb-2 { background: #ffe0cc; top: 8vw;  right: -10vw; animation: drift2 32s var(--ease) infinite; }
.orb-3 { background: #ecd9ff; bottom: -14vw; left: 10vw; animation: drift3 29s var(--ease) infinite; }
.orb-4 { background: #d3fbee; bottom: -8vw; right: 6vw;  animation: drift1 35s var(--ease) infinite reverse; }
html.dark .orb { opacity: .22; }
html.dark .orb-1 { background: #1d3a8f; }
html.dark .orb-2 { background: #7a3a1f; }
html.dark .orb-3 { background: #4a2a7a; }
html.dark .orb-4 { background: #0f5a4a; }
@keyframes drift1 { 0%,100%{transform:translate(0,0) scale(1)} 50%{transform:translate(6vw,4vw) scale(1.12)} }
@keyframes drift2 { 0%,100%{transform:translate(0,0) scale(1)} 50%{transform:translate(-5vw,5vw) scale(1.08)} }
@keyframes drift3 { 0%,100%{transform:translate(0,0) scale(1)} 50%{transform:translate(4vw,-5vw) scale(1.1)} }

/* ═══════════════════════════════════════════════
   TOP BAR — floating glass, 3 zones
═══════════════════════════════════════════════ */
.topbar {
  position: fixed; top: 12px; left: 50%;
  transform: translateX(-50%);
  z-index: 800;
  width: min(1160px, calc(100vw - 24px));
  height: var(--topbar-h);
  display: grid; grid-template-columns: auto auto auto;
  /* auto auto auto has no flexible track to soak up leftover width, so
     space-between does the job 1fr auto 1fr used to: any extra room goes
     into the two gaps around the center title, same math either way. */
  justify-content: space-between;
  align-items: center; gap: 8px;
}

/* Shared frosted-glass capsule */
.tb-cap {
  position: relative; isolation: isolate;
  height: var(--topbar-h);
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 100px;
  background: color-mix(in srgb, var(--surface) 62%, transparent);
  backdrop-filter: blur(20px) saturate(1.8);
  -webkit-backdrop-filter: blur(20px) saturate(1.8);
  border: 0.5px solid var(--glass-border);
  box-shadow: var(--glass-drop), var(--glass-edge);
  color: var(--t1);
  transition: transform .35s var(--spring), background .25s var(--ease);
}

/* Left — back arrow + wordmark */
.tb-back {
  justify-self: start;
  gap: 4px; padding: 0 22px 0 16px;
}
.tb-arrow { width: 19px; height: 19px; color: var(--t1); transition: transform .35s var(--spring); }
.tb-back-txt { font-size: 16px; font-weight: 600; letter-spacing: -.02em; }
@media (hover: hover) {
  .tb-back:hover { transform: translateY(-1px); background: color-mix(in srgb, var(--surface) 82%, transparent); }
  .tb-back:hover .tb-arrow { transform: translateX(-3px); }
}
/* pulsación: encoge rápido (.12s); al soltar rebota con el spring de .tb-cap.
   Declarado DESPUÉS del hover: misma especificidad, y durante el click el
   hover también aplica — el orden decide quién gana */
.tb-back:active { transform: scale(.96); transition-duration: .12s; }

/* Collapsed "TorBula" wordmark — JS-driven (index.js layoutTopbar()), not a
   fixed viewport breakpoint: .tb-compact gets added once the back capsule
   would sit within .topbar's own grid `gap` (8px) of the center title,
   whatever that width happens to be for the current title. */
.topbar.tb-compact .tb-back-txt { display: none; }
.topbar.tb-compact .tb-back { padding: 0; width: var(--topbar-h); justify-content: center; }
/* el chevron "<" tiene la masa visual a la derecha; sin texto, desplazarlo
   un pelín a la izquierda para que parezca centrado (centrado óptico) */
.topbar.tb-compact .tb-back .tb-arrow { position: relative; left: -1.5px; }

/* Center — section title */
.tb-center {
  justify-self: center;
  padding: 0 26px;
  font-size: 16px; font-weight: 600; letter-spacing: -.02em;
  color: var(--t1);
  cursor: default;
}

/* Right — language + theme toggles (circles) */
.tb-right { justify-self: end; display: flex; gap: 8px; }
.tb-theme {
  width: var(--topbar-h); padding: 0;
  overflow: hidden;
  cursor: pointer;
}
.tb-theme-img { width: 32px; height: 32px; }
html.dark .tb-theme-img { filter: invert(1); }
.tb-lang-img { width: 27px; height: 27px; }
@media (hover: hover) {
  .tb-theme:hover { transform: translateY(-1px); background: color-mix(in srgb, var(--surface) 82%, transparent); }
}
/* feedback de pulsación — mismo encogimiento que .nav-ic en el portfolio.
   SIEMPRE después del hover: en escritorio el click ocurre estando en hover,
   tienen la misma especificidad y el orden en el archivo decide quién gana */
.tb-theme:active { transform: scale(.9); transition-duration: .12s; }

/* ═══════════════════════════════════════════════
   TOOLS GRID
═══════════════════════════════════════════════ */
.tools-wrap {
  max-width: 1160px; margin: 0 auto;
  padding: calc(var(--topbar-h) + 48px) clamp(18px, 4vw, 40px) 80px;
}
.tools-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: clamp(18px, 2.4vw, 30px);
}

/* ── Tool card = a macOS app window ── */
.tool-card {
  position: relative; isolation: isolate;
  display: flex; flex-direction: column;
  width: 100%; text-align: left;
  border-radius: 14px; overflow: hidden;
  background: var(--surface);
  border: 0.5px solid var(--glass-border);
  box-shadow: 0 1px 4px rgba(0,0,0,.06), 0 14px 34px rgba(0,0,0,.10);
  opacity: 0; transform: translateY(26px) scale(.97);
  transition: transform .5s var(--spring2), box-shadow .5s var(--ease), opacity .5s var(--ease);
}
html.dark .tool-card { border-color: rgba(255,255,255,.1); }
.tool-card.animate-in { opacity: 1; transform: none; }
@media (hover: hover) {
  .tool-card:hover {
    transform: translateY(-6px) scale(1.015);
    box-shadow: 0 2px 10px rgba(0,0,0,.12), 0 30px 60px rgba(0,0,0,.18);
  }
  .tool-card:hover .tool-art { transform: scale(1.08); }
}
.tool-card:active { transform: translateY(-2px) scale(.99); }

/* Title bar — traffic lights + centered window title.
   Height (38px) and the image's aspect-ratio below match the portfolio's
   project cards (root index.css .win-bar / .project-card .win-body) so both
   card shapes read as the same "window" family. */
.win-bar {
  position: relative; flex-shrink: 0; height: 38px;
  display: flex; align-items: center;
  background: color-mix(in srgb, var(--surface) 92%, var(--t1));
  border-bottom: 0.5px solid var(--hair);
}
/* Two flexible spacers (pseudo-elements = flex items) frame the title so it
   centers in the whole window. The left one is floored at the dots' width, so
   when the window gets too narrow the title can't slide under the traffic
   lights — it shifts right and uses the room toward the right edge instead. */
.win-bar::before { content: ""; flex: 1 1 0; min-width: 60px; }
.win-bar::after  { content: ""; flex: 1 1 0; min-width: 12px; }
.win-dots {
  position: absolute; left: 12px;
  display: flex; align-items: center; gap: 7px;
}
.win-dot {
  width: 11px; height: 11px; border-radius: 50%;
  box-shadow: inset 0 0 0 .5px rgba(0,0,0,.14);
}
.win-red { background: #ff5f57; } .win-yellow { background: #febc2e; } .win-green { background: #28c840; }
.win-title {
  flex: 0 1 auto; min-width: 0; padding: 0 6px;
  text-align: center;
  font-size: 14px; font-weight: 500; color: var(--t2);
  letter-spacing: -.01em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* Window content — the screenshot when the tool has one, otherwise the icon
   (index.js renders exactly one of .tool-image / .tool-art, never both). */
.win-body {
  position: relative; flex: 1;
  aspect-ratio: 549 / 365; overflow: hidden;
  display: flex; align-items: center; justify-content: center;
  background:
    radial-gradient(120% 92% at 50% -10%, color-mix(in srgb, var(--blue) 12%, transparent), transparent 62%),
    linear-gradient(180deg, color-mix(in srgb, var(--t1) 4%, var(--surface)), var(--surface));
}
.tool-art {
  display: flex; align-items: center; justify-content: center;
  color: var(--t1); opacity: .9;
  filter: drop-shadow(0 6px 14px rgba(0,0,0,.14));
  transition: transform .6s var(--spring2);
}
.tool-art svg { height: 58px; width: auto; max-width: 58px; }
/* Screenshot per theme with a cross-fade (same technique as the portfolio's
   project cards): two stacked layers, light always underneath opaque, dark
   fades in on top when html.dark is set — the URLs come from tools-data.js. */
.tool-image {
  position: absolute; inset: 0;
  overflow: hidden;
}
.tool-image::before, .tool-image::after {
  content: '';
  position: absolute; inset: 0;
  background-size: cover; background-position: top center;
  transition: transform .6s var(--spring2);
}
.tool-image::before { background-image: var(--img-light); }
.tool-image::after {
  background-image: var(--img-dark, var(--img-light));
  opacity: 0;
  transition: opacity .5s var(--ease), transform .6s var(--spring2);
}
html.dark .tool-image::after { opacity: 1; }

/* ═══════════════════════════════════════════════
   LAUNCH MODAL
═══════════════════════════════════════════════ */
.tool-modal {
  position: fixed; inset: 0; z-index: 9999;
  display: flex; align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--bg) 64%, transparent);
  backdrop-filter: blur(26px) saturate(1.4);
  -webkit-backdrop-filter: blur(26px) saturate(1.4);
  animation: tm-fade .3s var(--ease);
}
@keyframes tm-fade { from { opacity: 0; } to { opacity: 1; } }
.tm-card {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 18px;
  width: 250px; min-height: 250px;
  padding: 32px 28px;
  border-radius: 30px;
  background: color-mix(in srgb, var(--surface) 80%, transparent);
  border: 0.5px solid var(--glass-border);
  box-shadow: var(--glass-drop), var(--glass-edge);
  animation: tm-pop .5s var(--spring2);
}
@keyframes tm-pop { from { opacity: 0; transform: translateY(14px) scale(.96); } to { opacity: 1; transform: none; } }
.tm-icon { color: var(--t1); }
.tm-icon svg { height: 56px; width: auto; max-width: 56px; }
.tm-title {
  font-size: 19px; font-weight: 600; letter-spacing: -.02em; color: var(--t1);
  text-align: center; line-height: 1.25; max-width: 100%; word-break: break-word;
}
.tm-spinner {
  width: 26px; height: 26px; border-radius: 50%;
  border: 2.5px solid var(--hair);
  border-top-color: var(--blue);
  animation: tm-spin .8s linear infinite;
}
@keyframes tm-spin { to { transform: rotate(360deg); } }
.tm-loading { font-size: 13px; font-weight: 500; color: var(--t2); }

/* Empty state */
.no-tools-message {
  grid-column: 1 / -1; text-align: center;
  padding: 80px 20px; color: var(--t2); font-size: 16px;
}
.no-tools-message .icon { font-size: 44px; display: block; margin-bottom: 14px; }

/* ═══════════════════════════════════════════════
   RESPONSIVE
═══════════════════════════════════════════════ */
@media (max-width: 640px) {
  .tb-center { font-size: 15px; padding: 0 12px; }
  .tools-grid { grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 16px; }
  .win-title { font-size: 14px; padding: 0 0 0 7px; }
  .tool-art svg { height: 44px; max-width: 44px; }
  .tb-back { padding: 0 12px 0 8px; }
}

@media (prefers-reduced-motion: reduce) {
  .orb { animation: none !important; }
  .tool-card { opacity: 1; transform: none; transition: box-shadow .3s var(--ease); }
  .tool-card.animate-in { transform: none; }
  .tm-card, .tool-modal { animation: none; }
  .tm-spinner { animation-duration: 1.4s; }
}
