/* Magic Bento — vanilla-JS port CSS (2026-07-11)
 *
 * Extracted verbatim from the tuned scratchpad playground. All selectors
 * are bento-scoped (`.card-grid` / `.magic-bento-card*` / `.particle*` /
 * `.global-spotlight` / `.bento-section`) — none of them collide with
 * any existing Novus dashboard CSS.
 *
 * The runtime entry lives in dashboard/features/magicBento.js
 * (exposes `window.mountMagicBento(rootEl, opts)`). GSAP is loaded via
 * CDN in dashboard.html before this file.
 *
 * Grid gaps have been widened from the upstream 0.5em (row/col) to
 * row-gap 2.25em / column-gap 4em — cards are 4:3 wide so equal `gap`
 * reads as tighter horizontally; asymmetric split gives comparable
 * visual breathing room in both directions. Change these two lines to
 * retune.
 */

.card-grid {
  display: grid;
  /* Tuned 2026-07-11 — user request to tighten further. Keeping the
     row-to-column 3:5 ratio the previous tune settled on (col slightly
     larger because cards are 4:3 wide → equal gap reads as tighter
     horizontally). row 1em / col 1.5em now — about 33% off the earlier
     row 1.5em / col 2.5em. Adjust the pair together to stay balanced. */
  row-gap: 1em;
  column-gap: 1.5em;
  padding: 0.75em;
  max-width: 54em;
  /*
   * One scaling law for the whole screen.
   *
   * This used to be 0.5vw, and it drove only the em-based SPACING —
   * every font-size inside a card was a fixed px, so the text did not
   * scale at all. A narrow window therefore got a proportionally
   * smaller grid holding the same-sized words: 171px cards with 19px
   * titles, which is where the crowding came from. And past ~1500px the
   * grid stopped widening while this kept growing, so a wider window
   * made the cards internally looser without making them bigger.
   *
   * cqw is a percentage of the CONTAINER's width — the mount whose
   * width the grid takes — so the type now scales with the thing it
   * sits in rather than with the window behind it. 2cqw of the 1200px
   * cap is 24px, which is what this reached before, so the wide layout
   * is unchanged to the pixel; below the cap everything shrinks
   * together and a narrow screen is the same picture, smaller.
   *
   * Every px inside a card is an em of this. The divisor is 24.
   *
   * The floor is 0.9rem rather than 1rem because the floor is where the
   * proportion breaks: hold the type up while the grid keeps shrinking
   * and the words no longer fit the box they were sized for. At 1rem
   * that happened at a 1024px window — the narrowest one that still
   * gets five columns — and a card overflowed by six pixels. 0.9rem
   * clears it, and below that the two-column layout has taken over.
   */
  font-size: clamp(0.8rem, 2cqw, 1.5rem);
}
.magic-bento-card {
  display: flex; flex-direction: column; justify-content: space-between;
  position: relative;
  aspect-ratio: 4/3;
  min-height: 200px;
  width: 100%; max-width: 100%;
  padding: 1.25em;
  border-radius: 20px;
  /* Novus theme (2026-07-11) — swap the upstream dark-purple palette
     for the same tokens the rest of the dashboard uses. --glow-color
     is the R,G,B triplet the border-glow / spotlight / particle rgba
     calls consume; JS can override via setProperty on each card. */
  border: 1px solid var(--border);
  background: var(--card);
  font-weight: 300;
  overflow: hidden;
  transition: all 0.3s ease;

  --glow-x: 50%;
  --glow-y: 50%;
  --glow-intensity: 0;
  --glow-radius: 200px;
  --glow-color: 74, 124, 111;
}
.magic-bento-card:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
}
.magic-bento-card__header,
.magic-bento-card__content {
  display: flex; position: relative; color: var(--text);
}
.magic-bento-card__header { gap: 0.75em; justify-content: space-between; }
.magic-bento-card__content { flex-direction: column; }
.magic-bento-card__label {
  font-size: 0.5em;
  color: var(--text3);
  font-weight: 500;
  letter-spacing: 0.03em;
  text-transform: uppercase;
}
.magic-bento-card__title,
.magic-bento-card__description { --clamp-title: 2; --clamp-desc: 2; }
/* Title is the visual anchor on SMALL cards (label + title + meta only)
   so it should read as the object name, not as a caption. Larger cards
   with a `primary` line get the same size — the primary sits directly
   below and takes on the "supporting fact" role. */
.magic-bento-card__title {
  font-weight: 600;
  font-size: 0.79167em;
  line-height: 1.25;
  margin: 0 0 0.15em;
  color: var(--text);
}
.magic-bento-card__description { font-size: 0.5em; line-height: 1.2; color: var(--text2); }

/* Editorial-anchor content layers (2026-07-11) — set by cardData fields
   `primary` / `secondary` / `meta`. Font-size + color step down in that
   order so the reader's eye finds the concrete anchor first, then the
   supporting fact, then the fade metadata. */
.magic-bento-card__primary {
  font-size: 0.60417em;
  line-height: 1.4;
  color: var(--text);
  font-weight: 500;
  margin: 0.35em 0 0.2em;
}
.magic-bento-card__secondary {
  font-size: 0.54167em;
  line-height: 1.35;
  color: var(--text2);
  margin: 0.2em 0 0;
}
.magic-bento-card__meta {
  font-size: 0.5em;
  line-height: 1.3;
  /* 2026-07-11 · brought up from --text3 to --text2 @ 0.82 so meta
     lines stay clearly readable ("legibly quiet", not "disabled"). */
  color: var(--text2);
  opacity: 0.82;
  margin: 0.55em 0 0;
  letter-spacing: 0.01em;
}

/* Paper link (2026-07-11) — clickable paper mention inside a card
   (title or meta). Uses a subtle underline that firms up on hover +
   colour swap to accent, so links read as "linkable" without shouting.
   The card's own click-ripple is suppressed on link clicks via
   stopPropagation in the JS handlers. */
.magic-bento-card__paper-link {
  cursor: pointer;
  text-decoration: underline;
  text-decoration-color: rgba(var(--glow-color), 0.35);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  transition: color 0.18s ease, text-decoration-color 0.18s ease;
}
.magic-bento-card__paper-link:hover {
  color: var(--accent);
  text-decoration-color: rgba(var(--glow-color), 1);
}

/* Stat block — horizontal row of "big-number + label" pairs. Used on
   Personal Graph to give scale metrics real visual weight instead of
   burying them in a comma-separated sentence. */
.magic-bento-card__stats {
  display: flex;
  flex-wrap: wrap;
  gap: 1.4em;
  margin: 0.6em 0 0.25em;
}
.magic-bento-card__stat {
  display: flex;
  flex-direction: column;
  gap: 0.08333em;
}
.magic-bento-card__stat-value {
  font-size: 0.91667em;
  font-weight: 600;
  color: var(--accent);
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
}
.magic-bento-card__stat-label {
  font-size: 0.45833em;
  color: var(--text3);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* (2026-07-11 · retired) Accent variant — used to give Claim Spotlight
   a sage-teal tint + stronger border + 3px left stripe as a hero
   treatment. Turned off per user request: all cards read as the same
   card. mountMagicBento still honours a `variant` field per card so a
   future protagonist card can add its own modifier — this ruleset is
   gone but the mechanism stays. */

/* Body split — big banner cards (Signals / Metrics / Claim) get
   `sideStats` on the right so the previously-empty right half of the
   card fills with a couple of large numbers. Body sits at the bottom
   of the card (card has justify-content: space-between); inside, left
   content grows, right side-stats stay intrinsic width and align to
   the bottom for consistent baseline with the meta line. */
.magic-bento-card__body--split {
  display: block;
  width: 100%;
}
.magic-bento-card__body--split .magic-bento-card__content {
  min-width: 0;
}
/* 2026-07-11 · side-stats pinned to card right-bottom via absolute
   positioning so overflow-hidden on the card body cannot clip the
   label line when left-side content runs long (was: flex-end inside
   body-split → CLAIMS card ate the SOURCES/PAPERS labels). Background
   set to var(--card) so long primary text visually stops at the number
   block instead of showing through underneath. */
.magic-bento-card__side-stats {
  position: absolute;
  right: 0.7em;
  bottom: 0.7em;
  display: flex;
  flex-direction: row;
  gap: 1.1em;
  flex-shrink: 0;
  z-index: 2;
  background: var(--card);
  padding: 0.25em 0 0 0.6em;
  border-radius: 6px;
}
.magic-bento-card__side-stat {
  display: flex;
  flex-direction: column;
  gap: 0.125em;
  align-items: flex-start;
}
.magic-bento-card__side-stat-value {
  font-size: 0.75em;
  font-weight: 700;
  color: var(--accent);
  line-height: 1.05;
  font-variant-numeric: tabular-nums;
}
.magic-bento-card__side-stat-label {
  font-size: 0.35417em;
  color: var(--text3);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

/* Thread — labelled 2-line callout, used on Personal Graph to lift
   "Most active thread" out of the meta line into its own module. */
.magic-bento-card__thread {
  margin-top: 0.7em;
}
.magic-bento-card__thread-label {
  font-size: 0.45833em;
  color: var(--text3);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  margin-bottom: 0.125em;
}
.magic-bento-card__thread-value {
  font-size: 0.58333em;
  color: var(--text);
  font-weight: 500;
  line-height: 1.3;
}

.magic-bento-card--text-autohide .magic-bento-card__title,
.magic-bento-card--text-autohide .magic-bento-card__description {
  display: -webkit-box; -webkit-box-orient: vertical;
  overflow: hidden; text-overflow: ellipsis;
}
.magic-bento-card--text-autohide .magic-bento-card__title {
  -webkit-line-clamp: var(--clamp-title); line-clamp: var(--clamp-title);
}
.magic-bento-card--text-autohide .magic-bento-card__description {
  -webkit-line-clamp: var(--clamp-desc); line-clamp: var(--clamp-desc);
}

@media (max-width: 599px) {
  .card-grid { grid-template-columns: 1fr; width: 90%; margin: 0 auto; padding: 0.5em; }
  .magic-bento-card { width: 100%; min-height: 180px; }
}
@media (min-width: 600px) { .card-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) {
  .card-grid { grid-template-columns: repeat(4, 1fr); }
  .magic-bento-card:nth-child(3) { grid-column: span 2; grid-row: span 2; }
  .magic-bento-card:nth-child(4) { grid-column: 1 / span 2; grid-row: 2 / span 2; }
  .magic-bento-card:nth-child(6) { grid-column: 4; grid-row: 3; }

  /* ────── Wide variant · 5×3 = 15-cell grid with 8 cards (2026-07-11
   *        editorial-hierarchy pass). Layout:
   *          row1: [Signals 3×1     ][Claim Spotlight 2×1 ]
   *          row2: [Benchmark 3×1   ][Ideas 1×1][Audit 1×1]
   *          row3: [Field 1×1][Formula 1×1][Personal Graph 3×1]
   *        Grid track lines are shared → outer perimeter is a clean
   *        rectangle, every internal edge is pixel-aligned. Max-width
   *        drops to 100% so mount-wide's 1200px is the sole width cap
   *        (shared with .expl-bento-header) — that way the section
   *        header's text left edge and the leftmost card's left edge
   *        land on the same pixel. Padding forced to 12px to match. */
  .expl-bento-screen-wide .card-grid {
    grid-template-columns: repeat(4, 1fr);
    /* A floor, not a fixed height. Everything else on this screen scales
       continuously with the container; the number of LINES a description
       takes does not — narrow the Personal card from 691px to 651px and
       its text finds one more line, which a fixed row clipped by eight
       pixels. The row grows for that case and is unchanged everywhere
       else, since the content otherwise fits. */
    grid-auto-rows: minmax(8.33333em, auto);
    max-width: 100%;
    padding: 0.5em;
  }
  .expl-bento-screen-wide .magic-bento-card {
    aspect-ratio: auto;
    min-height: 0;
  }
  /* 4×3 = 12 cells, the same eight cards and the same hierarchy — the
     wide cards stay wide, the 1×1s stay 1×1, and the perimeter is still
     a rectangle. Five columns only pay off when there is room for them:
     the layout was drawn at a 1200px grid, where a column is 206px, and
     below that five columns get narrower than they were ever meant to
     be. Four keeps the column near its designed width on a laptop.
       row1: [Signals 2×1][Claims  2×1]
       row2: [Metrics 2×1][Ideas][Audit]
       row3: [Explore][Methods][Personal 2×1] */
  .expl-bento-screen-wide .magic-bento-card:nth-child(1) { grid-column: 1 / span 2; grid-row: 1; } /* Signals    */
  .expl-bento-screen-wide .magic-bento-card:nth-child(2) { grid-column: 3 / span 2; grid-row: 1; } /* Claim Spot */
  .expl-bento-screen-wide .magic-bento-card:nth-child(3) { grid-column: 1 / span 2; grid-row: 2; } /* Benchmark  */
  .expl-bento-screen-wide .magic-bento-card:nth-child(4) { grid-column: 3;          grid-row: 2; } /* Ideas      */
  .expl-bento-screen-wide .magic-bento-card:nth-child(5) { grid-column: 4;          grid-row: 2; } /* Audit      */
  .expl-bento-screen-wide .magic-bento-card:nth-child(6) { grid-column: 1;          grid-row: 3; } /* Field Map  */
  .expl-bento-screen-wide .magic-bento-card:nth-child(7) { grid-column: 2;          grid-row: 3; } /* Formula    */
  .expl-bento-screen-wide .magic-bento-card:nth-child(8) { grid-column: 3 / span 2; grid-row: 3; } /* Personal   */
}
/* ────── Five columns, from 1600px up ──────
 * The original arrangement, unchanged, behind a width where it fits.
 * A column here is 206px at the 1200px cap the grid stops at; below
 * 1600 the window cannot give five of them that much, so four is what
 * is drawn instead. Wide windows see exactly what they saw before —
 * this block is the whole of the difference. */
@media (min-width: 1600px) {
  .expl-bento-screen-wide .card-grid { grid-template-columns: repeat(5, 1fr); }
  .expl-bento-screen-wide .magic-bento-card:nth-child(1) { grid-column: 1 / span 3; grid-row: 1; } /* Signals    */
  .expl-bento-screen-wide .magic-bento-card:nth-child(2) { grid-column: 4 / span 2; grid-row: 1; } /* Claim Spot */
  .expl-bento-screen-wide .magic-bento-card:nth-child(3) { grid-column: 1 / span 3; grid-row: 2; } /* Benchmark  */
  .expl-bento-screen-wide .magic-bento-card:nth-child(4) { grid-column: 4;          grid-row: 2; } /* Ideas      */
  .expl-bento-screen-wide .magic-bento-card:nth-child(5) { grid-column: 5;          grid-row: 2; } /* Audit      */
  .expl-bento-screen-wide .magic-bento-card:nth-child(6) { grid-column: 1;          grid-row: 3; } /* Field Map  */
  .expl-bento-screen-wide .magic-bento-card:nth-child(7) { grid-column: 2;          grid-row: 3; } /* Formula    */
  .expl-bento-screen-wide .magic-bento-card:nth-child(8) { grid-column: 3 / span 3; grid-row: 3; } /* Personal   */
}


/* Border glow — driven by --glow-x/-y/-intensity/-radius set by the
   GlobalSpotlight loop in magicBento.js. --glow-color is a fallback-safe
   `R, G, B` triplet so we can retheme by setProperty without editing
   the gradient. Novus default is sage teal (74,124,111 = --accent). */
.magic-bento-card--border-glow::after {
  content: ''; position: absolute; inset: 0; padding: 3px;
  background: radial-gradient(
    var(--glow-radius) circle at var(--glow-x) var(--glow-y),
    rgba(var(--glow-color), calc(var(--glow-intensity) * 0.8)) 0%,
    rgba(var(--glow-color), calc(var(--glow-intensity) * 0.4)) 30%,
    transparent 60%
  );
  border-radius: inherit;
  -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  mask-composite: exclude;
  pointer-events: none; opacity: 1;
  transition: opacity 0.3s ease; z-index: 1;
}
.magic-bento-card--border-glow:hover::after { opacity: 1; }
.magic-bento-card--border-glow:hover {
  box-shadow:
    0 4px 20px rgba(28, 58, 47, 0.10),
    0 0 30px rgba(var(--glow-color), 0.25);
}
.particle-container { position: relative; overflow: hidden; }
.particle::before {
  content: ''; position: absolute;
  top: -2px; left: -2px; right: -2px; bottom: -2px;
  background: rgba(var(--glow-color), 0.2);
  border-radius: 50%; z-index: -1;
}
.particle-container:hover {
  box-shadow:
    0 4px 20px rgba(28, 58, 47, 0.08),
    0 0 30px rgba(var(--glow-color), 0.2);
}
.global-spotlight {
  mix-blend-mode: screen;
  will-change: transform, opacity;
  z-index: 200 !important;
  pointer-events: none;
}
.bento-section { position: relative; user-select: none; }
