/* ══════════════════════════════════════════════════════
   explore.css — the drill-down view

   Sized for a phone first and allowed to widen, because the failure this
   replaces was a layout that only worked at desktop proportions. Everything
   here is ordinary block flow: no absolute positioning, no camera, nothing
   that can leave the screen.
   ══════════════════════════════════════════════════════ */

#explore {
  position: fixed;
  inset: 0;
  top: var(--ex-top, 108px);   /* clear of the title and the floating buttons */
  bottom: var(--ex-bottom, 0px);
  z-index: 30;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 8px 14px 90px;
  background: var(--bg);
  color: var(--text-primary);
  font-family: var(--font-sans);
}
body[data-view="map"] #explore { display: none; }
/* Map furniture, hidden here because none of it drives this view.
   #timeline is the one that mattered: the era slider filters the canvas and
   has no effect on a card grid, but it is fixed to the bottom of the window
   and paints above .ex-path — so the path dots and the "you are here" label
   were behind it at every point, on both viewports, from the day they were
   written. Explore answers three questions and "where am I" is one of them;
   it was answering it underneath 79px of another view's chrome.

   elementFromPoint is what found it. explore:says-where-you-are reads the
   labels out of the DOM and passed the whole time — a control can be present,
   correct, translated and completely invisible.

   Hiding the strip is not free, and the cost is not in this file: it cannot
   measure itself while display:none, so every rebuild it receives here — from
   a language switch, a theme toggle, or start-up, which builds it after the
   shell has already been chosen — is silently skipped, and it is revealed
   still carrying the last thing it computed. setShellView() in js/app.js
   rebuilds it on the way back into the map. Hide anything else that sizes
   itself from its own box and it needs the same. */
body[data-view="explore"] #svg,
body[data-view="explore"] #zoom-ctrl,
body[data-view="explore"] #timeline,
body[data-view="explore"] .reveal-panel { display: none !important; }

/* ── Back ── */
.ex-head { margin-bottom: 10px; }
.ex-back {
  display: inline-flex; align-items: center; gap: 6px;
  min-height: 44px;               /* a thumb target, not a mouse target */
  padding: 0 14px 0 8px;
  background: transparent; border: 0;
  color: var(--accent); font-size: 15px; font-weight: 600;
  font-family: inherit; cursor: pointer;
}
.ex-back span { font-size: 22px; line-height: 1; }
.ex-back-root { color: var(--text-muted, var(--text-secondary)); font-weight: 500; }

/* ── The tree itself ──
   One column, not a grid. A grid cannot nest, and nesting is the whole point:
   a child has to sit visibly underneath the thing it came out of. On a desktop
   the column is capped rather than stretched, because a 1400px-wide row makes
   the structure that carries the shape invisible.

   The nesting used to be carried by indent alone, and by an indent that was
   capped at four steps — the tree runs ten deep, and ten real steps of margin
   eats a 390px phone. So levels 5 through 10 all rendered at the same offset,
   and the deepest half of the tree was drawn perfectly flat. Worse, indent on
   its own is what a nested *list* looks like: nothing on the page joined a row
   to the row it came out of, so the whole view read as an outline with good
   manners rather than as a tree.

   It is drawn now — trunks and limbs, below — which costs one narrow gutter
   per level instead of a whole indent step, so the cap could go. */
.ex-tree {
  --ex-step: 15px;      /* what one level of nesting costs horizontally */
  --elbow-r: 11px;      /* how tightly a limb turns out of its trunk */
  /* How solid the drawing is. Named rather than inlined because the light
     theme needs different numbers and the reason is physical: the clade
     colours are bright strokes chosen against a near-black ground, and the
     same alpha over cream gives a pastel that disappears. The dimmed limbs
     went first — Chondrichthyes and Sarcopterygii were a suggestion of a line
     rather than a line. */
  --limb-a: 0.6;
  --limb-dim-a: 0.34;
  --limb-open-a: 0.9;
  --trunk-a: 0.13;      /* at the root … */
  --trunk-gain: 0.26;   /* … plus this much by the level you are standing on */
  max-width: 760px;
}
[data-theme="light"] .ex-tree {
  --limb-a: 0.75; --limb-dim-a: 0.52; --limb-open-a: 1;
  --trunk-a: 0.2; --trunk-gain: 0.3;
}

/* One row. No indent of its own any more: the nesting is drawn by .ex-kids,
   which is the element that actually knows it is a level. */
.ex-card {
  position: relative;
  display: flex; align-items: center; gap: 10px;
  width: 100%; padding: 8px 10px;
  text-align: start;
  border: 0; border-inline-start: 2px solid transparent;
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--text-primary); font-family: inherit; cursor: pointer;
  transition: background 140ms var(--ease-standard);
}
.ex-card:hover, .ex-card:focus-visible { background: rgba(127,127,127,0.10); }

/* ══ Trunks and limbs ══════════════════════════════════════════════════
   A level of children is one gutter wide. The trunk runs down that gutter and
   each child reaches out of it on a curve.

   Both are borders on pseudo-elements rather than an SVG or a background
   image, for three reasons: they take their weight and colour from custom
   properties the renderer already sets per node, they need no second source of
   truth for geometry, and they flip in Hebrew for free — every offset here is
   a logical property, so the tree grows from the right edge in RTL without a
   single mirrored rule.

   The weights are the point. A limb's thickness is drawn from how much life
   hangs below it (js/taxonRank.js: subtreeSize), on a log scale, so Mammals
   leaves its parent visibly heavier than Chondrichthyes does — the same fact
   the subtitle states in words, in the form a tree states it. A trunk is
   always at least as heavy as any limb leaving it, because its subtree
   contains theirs. */
.ex-kids { padding-inline-start: var(--ex-step); }

.ex-branch { position: relative; --row-mid: 22px; }
.ex-branch-lv1 { --row-mid: 32px; }
.ex-more { position: relative; --row-mid: 18px; }

/* The trunk: one continuous run down the gutter, in the *parent's* weight and
   colour, past every child. It stops at the last child's elbow rather than
   overshooting into empty space — which is the whole difference between a tree
   and a bracket. */
.ex-kids > .ex-branch::after,
.ex-kids > .ex-more::after {
  content: ''; position: absolute; pointer-events: none;
  inset-inline-start: calc(-1 * var(--ex-step));
  top: 0; bottom: 0;
  border-inline-start: var(--tw, 1.5px) solid var(--tc, var(--accent));
  /* Receding. Eight nested levels drawn at one opacity are eight identical
     lines down the side of a phone, and the reader cannot tell which of them
     is the one they are standing in. --near runs 0 at the root to 1 at the
     open level, so the trunk beside the current row is solid and the ones it
     descends from fall back into the page. */
  opacity: calc(var(--trunk-a) + var(--trunk-gain) * var(--near, 1));
}
.ex-kids > :last-child::after { bottom: auto; height: var(--row-mid); }

/* The limb: out of the trunk and into the row, on a curve. Drawn in the
   child's own weight and its own clade colour, so a heavy branch leaves a
   visible knot and a single species leaves a hair.

   Hung on the row rather than on the branch, and that is not a detail: a
   branch box spans the row *and its whole open subtree*, so a percentage of it
   is meaningless and the elbow had to be placed at a guessed pixel offset
   instead. It drifted the moment a subtitle wrapped to two lines — "Family · 5
   inside · 3 levels deep" does exactly that on a 390px phone — and the limb
   attached somewhere above the row it belonged to. The row's own box is the
   thing whose middle is worth knowing, so 50% of it is exact at any height. */
.ex-kids > .ex-branch > .ex-card::before,
.ex-kids > .ex-more::before {
  content: ''; position: absolute; pointer-events: none;
  inset-inline-start: calc(-1 * var(--ex-step));
  /* A heavy limb turns in a wider arc than a twig does, which is the one piece
     of branch physics that costs nothing to honour: the sweep is driven by the
     same weight as the stroke, so a thick branch leaves its trunk on a long
     curve and a single species leaves on a tight one. Without it every join in
     the tree turned at an identical drafted radius, which is most of what made
     the drawing feel machined. */
  top: calc(50% - var(--elbow-r) - var(--bw, 1.5px));
  width: var(--ex-step); height: calc(var(--elbow-r) + var(--bw, 1.5px));
  border-inline-start: var(--bw, 1.5px) solid var(--bc, var(--accent));
  border-block-end: var(--bw, 1.5px) solid var(--bc, var(--accent));
  border-end-start-radius: calc(var(--elbow-r) + var(--bw, 1.5px));
  opacity: var(--limb-a);
}
/* The truncation stub belongs to the level, not to the branch above it, and
   custom properties inherit — without this it borrowed the parent's weight and
   the "35 more" run was drawn as heavy as Mammals itself. */
.ex-more { --bw: 1.25px; }

/* The lineage you are standing on is the trunk of the picture, so it is drawn
   at full strength; the branches passed over stay visible but recede. Both
   still hang off the same trunk — the roads not taken are half of what makes
   a tree legible, and fading them out entirely would leave a corridor. */
.ex-kids > .ex-branch.open > .ex-card::before { opacity: var(--limb-open-a); }
.ex-kids > .ex-branch.open::after { opacity: calc(var(--trunk-a) + 0.45 * var(--near, 1)); }
.ex-kids > .ex-branch.dim > .ex-card::before { opacity: var(--limb-dim-a); }

/* A level that has been cut short says so on the trunk. The run into the "35
   more" button is dashed, because the growth it leads to is real and simply
   not drawn — a solid line there claims the tree ends at eight children when
   Mammals has forty-three. */
.ex-kids > .ex-more::before,
.ex-kids > .ex-more::after { border-style: dashed; opacity: 0.45; }

/* Depth 1 is the front door and keeps a rich row. Deeper rows are compact —
   the old 132px cards stacked nine deep did not fit the device this view was
   built for. */
.ex-card-lv1 { min-height: 64px; }
.ex-card-lv1 .ex-card-media { width: 36px; height: 36px; }
.ex-card-lv1 .ex-card-name { font-size: 16px; }
.ex-card-deep { min-height: 44px; }
.ex-card-deep .ex-card-media { width: 30px; height: 30px; }
.ex-card-deep .ex-card-name { font-size: 14px; }

/* The open lineage. The marker is what makes the chain readable at a glance
   once several levels are showing. */
.ex-card.open {
  border-inline-start-color: var(--cc, var(--accent));
  background: rgba(127,127,127,0.07);
}
.ex-card.open .ex-card-name { font-weight: 700; }

/* A branch that was passed over. Still on the page, still tappable — which is
   why this is a colour token and not an opacity: a dimmed control that a
   finger can still reach gets no WCAG exemption, and --text-secondary is
   measured at 8.3:1 dark and 8.6:1 light. The media may fade, since a picture
   carries no text for the sweep to measure. */
.ex-card.dim .ex-card-name { color: var(--text-secondary); font-weight: 500; }
.ex-card.dim .ex-card-media { opacity: 0.55; filter: grayscale(0.8); }

.ex-card-media {
  display: flex; align-items: center; justify-content: center;
  flex: 0 0 auto;
}
.ex-card-img { width: 100%; height: 100%; object-fit: cover; border-radius: 50%;
  border: 2px solid var(--cc, var(--accent)); }
.ex-card-emoji { font-size: 22px; line-height: 1; }
/* Masked, not filled: the file paints itself black and we want the taxon's
   colour. Safe here because it is a plain block, not a foreignObject. */
.ex-card-sil {
  width: 100%; height: 100%; display: block;
  background-color: var(--sc, var(--accent));
  mask-repeat: no-repeat; -webkit-mask-repeat: no-repeat;
  mask-position: center; -webkit-mask-position: center;
  mask-size: contain; -webkit-mask-size: contain;
}
.ex-card-text { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1 1 auto; }
.ex-card-name { font-weight: 650; line-height: 1.2; }
.ex-card-sub { font-size: 11.5px; color: var(--text-secondary); }
.ex-card-chev {
  flex: 0 0 auto; margin-inline-start: auto;
  color: var(--text-muted, var(--text-secondary)); font-size: 16px; line-height: 1;
}

/* What you just opened, revealed where you opened it. Only the deepest open
   node gets one, so this never stacks. */
.ex-reveal {
  display: flex; gap: 12px; align-items: flex-start;
  margin: 2px 0 6px;
  padding: 10px 12px;
  border: 1px solid var(--glass-border);
  border-inline-start: 3px solid var(--cc, var(--accent));
  border-radius: var(--radius-lg);
  background: var(--surface, rgba(255,255,255,0.03));
}
.ex-hero-img { width: 76px; height: 76px; flex: 0 0 auto;
  object-fit: cover; border-radius: var(--radius-md); }
.ex-reveal-text { min-width: 0; }
.ex-latin { margin: 0; font-style: italic; font-size: 13px; color: var(--text-secondary); }
.ex-desc {
  margin: 6px 0 0; font-size: 14px; line-height: 1.45;
  color: var(--parchment, var(--text-secondary));
  /* Clamped: LUCA's description alone filled a phone screen and pushed every
     row below the fold, which is the one thing this view exists to prevent.
     The full text is in the detail panel. */
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 4;
  overflow: hidden;
}

/* Wide levels are cut off — mammals has 43 children and unfolding all of them
   buries every branch below it. */
.ex-more {
  display: block; margin: 2px 0 4px;
  padding: 6px 10px; min-height: 36px;
  background: transparent; border: 1px dashed var(--glass-border);
  border-radius: var(--radius-md);
  color: var(--text-secondary); font-family: inherit; font-size: 12.5px;
  cursor: pointer;
}
.ex-more:hover { color: var(--text-primary); border-color: var(--accent); }

/* The unfold. Only the branch that was just opened animates — re-rendering
   replays every animation on the page, so without this marker each tap made
   the whole column shimmer. */
.ex-kids.ex-just-opened { animation: ex-unfold 170ms var(--ease-standard); }
@keyframes ex-unfold {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: none; }
}

/* ── How far down am I ──
   Dots, not text: it has to survive a nine-deep lineage on a narrow screen,
   and the names are already in the header and the cards. */
.ex-path {
  position: fixed; inset-inline: 0; bottom: 0;
  display: flex; justify-content: center; align-items: center; gap: 7px;
  padding: 12px; z-index: 2;
  background: linear-gradient(to top, var(--bg) 55%, transparent);
}
.ex-step {
  width: 22px; height: 22px; padding: 0;
  display: grid; place-items: center;
  background: none; border: 0; cursor: pointer;
}
.ex-step span {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--text-secondary); opacity: 0.4;
  transition: transform 140ms var(--ease-standard), opacity 140ms var(--ease-standard);
}
.ex-step:hover span { opacity: 0.8; }
.ex-step.current span {
  background: var(--accent); opacity: 1; transform: scale(1.7);
}

/* Desktop has room for the hero to breathe and for more columns. */
@media (min-width: 780px) {
  #explore { top: var(--ex-top, 64px); padding: 14px 28px 90px; }
  .ex-hero-img { width: 110px; height: 110px; }
  /* A wider gutter where there is room for one: the step has to stay tight on
     a 390px phone, where ten levels of it compete with the names. */
  .ex-tree { max-width: 820px; --ex-step: 20px; --elbow-r: 13px; }
  .ex-desc { -webkit-line-clamp: 5; }
}

/* The left rail is a fixed column from 769px up (below that it collapses to a
   ☰ button and is display:none until opened). Explore is ordinary block flow
   across the whole viewport, so without this the hero title, its description
   and the whole first domain card render underneath the rail — on the default
   view, at first paint. It never showed on a phone, which is where this view
   was designed.

   Logical property, and keyed to the rail's own breakpoint rather than the
   780px one below: in Hebrew the rail pins to the right, and padding-inline-
   start follows it there. Same expression the search box already uses to
   clear the rail (chrome.css). Declared after the 780px block because that
   block sets the `padding` shorthand, which would otherwise reset this. */
@media (min-width: 769px) {
  #explore { padding-inline-start: calc(var(--chrome-pad) + var(--rail-w) + 20px); }
}

@media (prefers-reduced-motion: reduce) {
  .ex-card, .ex-step span { transition: none; }
  .ex-kids.ex-just-opened { animation: none; }
}

/* ── "You are here" ──
   The dots alone said how deep you were but not what you were standing on, and
   the native `title` they carried is unreachable on a phone — a tooltip that
   needs a mouse is no tooltip on the device this view was built for.

   So the current step is labelled permanently, and the others name themselves
   on hover or keyboard focus. Both are CSS on a data attribute, which keeps
   this out of the CSP's way — an inline handler here would not run at all. */
.ex-path { flex-wrap: wrap; gap: 7px 7px; }
.ex-here {
  flex-basis: 100%;
  text-align: center;
  margin-top: 2px;
  font-size: 12px; font-weight: 600;
  color: var(--accent);
  letter-spacing: 0.01em;
}

.ex-step { position: relative; }
.ex-step::after {
  content: attr(data-name);
  position: absolute; bottom: calc(100% + 6px); left: 50%;
  transform: translateX(-50%) translateY(3px);
  padding: 4px 9px; border-radius: var(--radius-sm);
  background: var(--surface, #12161c);
  border: 1px solid var(--glass-border);
  color: var(--text-primary);
  font-size: 12px; font-weight: 600; white-space: nowrap;
  opacity: 0; pointer-events: none;
  transition: opacity 120ms var(--ease-standard), transform 120ms var(--ease-standard);
  z-index: 3;
}
.ex-step:hover::after,
.ex-step:focus-visible::after {
  opacity: 1; transform: translateX(-50%) translateY(0);
}
/* The current step already has its name printed below; a second copy floating
   above it is noise. */
.ex-step.current::after { content: none; }

@media (prefers-reduced-motion: reduce) {
  .ex-step::after { transition: none; }
}
