/* ============================================================================
   PROJECTS INDEX — lattice layer
   ----------------------------------------------------------------------------
   Loaded after css/lattice.css, which supplies the shared pieces: the fixed
   void / canvas / veil plates, the rail, the section index, and the
   visibility gate that keeps all of it hidden unless the field is actually
   running. Only what is specific to this page lives here.

   THIS PAGE IS THE INVERSE OF THE HOMEPAGE
   The homepage problem was too much opacity: full-width section plates and
   component cards stacked to ~0.94, so the field could not reach the reader.
   Here the problem is the opposite. Audited across all 19 viewports, the
   catalogue rows carry almost no ground of their own:

     .portfolio-tier-1-row   transparent
     .portfolio-t2-card      rgba(255,255,255,.03)
     .portfolio-t3-row       rgba(201,169,89,.03)

   So the moment <body> goes transparent to reveal the canvas, 49 rows of
   dense body copy would be sitting directly on the structure. The fix is to
   ADD a plate under the catalogue rather than open one up, and to hold the
   field far back through it. The hero and the closing CTA — the two regions
   that are mostly display type — are where it is allowed to be loud.
   ========================================================================== */

/* ── the catalogue plate ────────────────────────────────────────────────────
   One continuous ground from the filter bar to the end of the methodology
   section. Applied to the sections rather than to each row so that the gaps
   between rows are covered too — a plate with holes in it reads as noise
   behind a list, which is exactly what a 49-row table must not have. */
html.has-lattice .archive-grid,
html.has-lattice .portfolio-tier-1-section,
html.has-lattice .portfolio-tier-2-section,
html.has-lattice .portfolio-tier-3-section {
  background: rgba(9, 12, 21, .66);
}

/* The nav already carries its own near-solid ground; it only needs to sit
   above the fixed layers. */
/* .project-nav is deliberately absent — see the note in lattice.css. It ships
   position:fixed; overriding it to relative un-stuck the header and buried the
   language dropdown under the hero. */
html.has-lattice main,
html.has-lattice .portfolio-hero,
html.has-lattice footer { position: relative; z-index: 1; }

/* ── where the field is allowed to be seen ──────────────────────────────────
   The five philosophy tiles are solid #060810 in the shipped page. Opening
   them turns each one into a window onto the structure — which is the right
   metaphor for a grid whose whole subject is one problem viewed at five
   scales. Their type is large and the plate is still dense, so this costs
   nothing measurable. */
html.has-lattice .portfolio-philosophy-tile {
  background: rgba(6, 8, 16, .74);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
}

/* The closing call to action is the one place the page stops arguing, so the
   field comes back up underneath it. */
html.has-lattice .portfolio-conversion-cta {
  background: linear-gradient(180deg, rgba(6, 8, 16, .55), rgba(6, 8, 16, .86));
}

/* ── field strength ─────────────────────────────────────────────────────────
   A flat value across a 15,000px page was wrong in both directions at once:
   too weak in the hero, where there is nothing to read but a headline and five
   tiles, and still competing through the catalogue. projects-index.js now sets
   this per scroll region — full in the hero, held back through the rows,
   opened again at the closing CTA. The value here is only the pre-JS default. */
html.has-lattice #ed-gl {
  opacity: .62;
  transition: opacity 780ms cubic-bezier(.25, .1, .25, 1);
}

/* THE PULSE
   The one moment the structure is allowed to take the foreground. Changing
   lens means the reader is looking, not reading — so the field surfaces for
   the length of the reorganisation and then gets out of the way. */
html.pjx-pulse.has-lattice #ed-gl { opacity: .95; transition-duration: 260ms; }

/* ── search ────────────────────────────────────────────────────────────────
   Injected by projects-index.js so projects.html itself is never edited. */
.pjx-search {
  position: relative;
  display: flex;
  align-items: center;
  gap: 10px;
  max-width: 520px;
  margin: 0 0 18px;
  padding: 0 14px;
  border: 1px solid var(--accent-18, rgba(201, 169, 89, .18));
  border-radius: 999px;
  background: rgba(6, 8, 16, .72);
  transition: border-color 200ms var(--ease, ease);
}

.pjx-search:focus-within { border-color: var(--accent-50, rgba(201, 169, 89, .5)); }

.pjx-search__label {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: .58rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--accent, #c9a959);
  white-space: nowrap;
}

.pjx-search__input {
  flex: 1;
  min-width: 0;
  padding: 11px 0;
  border: 0;
  background: none;
  color: #fff;
  font: inherit;
  font-size: .86rem;
}

.pjx-search__input::placeholder { color: rgba(255, 255, 255, .40); }
.pjx-search__input:focus { outline: none; }
.pjx-search__input::-webkit-search-cancel-button { filter: invert(1) opacity(.5); }

.pjx-search__kbd {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: .64rem;
  line-height: 1;
  padding: 4px 7px;
  border: 1px solid rgba(255, 255, 255, .18);
  border-radius: 5px;
  color: rgba(255, 255, 255, .55);
}

/* On a touch device there is no "/" to press, so the hint would be a lie. */
@media (hover: none) { .pjx-search__kbd { display: none; } }

/* ── empty state ──────────────────────────────────────────────────────────── */
.pjx-empty {
  margin: 28px 0 40px;
  padding: 22px 24px;
  border: 1px dashed var(--accent-22, rgba(201, 169, 89, .22));
  border-radius: 12px;
  color: rgba(255, 255, 255, .68);
  font-size: .9rem;
}

/* ── filter entrance ───────────────────────────────────────────────────────
   Deliberately almost empty. The entrance reuses .fade-in-scale/.revealed,
   which main.min.css already defines and the page already ships — introducing
   a second opacity system here would mean two rules fighting over the same
   property, and would leave every card invisible if the controller failed to
   load. See projects-index.js for the re-reveal.

   The only thing CSS has to add is making [hidden] stick: several of these
   rows are display:grid or display:flex in the page's own stylesheet, and an
   author-level display always beats the user-agent [hidden] rule. */
[data-category][hidden] { display: none !important; }

/* ── reduced motion ────────────────────────────────────────────────────────
   No stagger, no pulse, no entrance. The controller already skips the
   timers; this makes sure nothing can transition even if a class lands. */
@media (prefers-reduced-motion: reduce) {
  html.has-lattice #ed-gl { transition: none; }
  html.pjx-pulse.has-lattice #ed-gl { opacity: .30; }
}

/* ── narrow ────────────────────────────────────────────────────────────────
   Matches lattice.css: the rail is hidden before the field itself stops. */
@media (max-width: 1180px) { .ed-sidx { display: none; } }
@media (max-width: 720px)  { .ed-rail { display: none; } }
