/* Page layouts: portada, directorio, ficha, proyecto — and the 390px behaviour. */

/* ---------------------------------------------------------------- portada */

.ap-hero {
  position: relative;
  background: var(--cielo-alto);
  /* The sky is bright, so the hero's own ink is night. Everything readable
     lives on the plate anyway; this is the safety net for anything that
     someday does not. */
  color: var(--noche);
  overflow: hidden;
  padding: 0 0 64px;
  /* Room for the tree. Without a floor the hero is only as tall as the plate,
     and the canopy gets cropped to a hedge. */
  min-height: min(780px, 84vh);
}
/* The sky the tree stands against: the prototype's, sun low behind the trunk.
   The one gradient in the system, and the reason the hero is not a flat green
   plate — the tree needs somewhere to be. */
.ap-hero__bg {
  position: absolute;
  inset: 0;
  background: radial-gradient(120% 92% at 50% 104%,
    var(--sol-1) 0%, var(--sol-2) 22%, var(--sol-3) 40%,
    var(--cielo) 76%, var(--cielo-alto) 100%);
}
/* Host for static/js/tree.js. Empty without JavaScript, and the hero still
   reads: sky, plate, type. Nothing here is content. */
.ap-hero__tree {
  position: absolute;
  /* Exactly the hero box: the script sizes the drawing from this element, so
     any overhang is drawn and then clipped — wasted pixels and a cropped crown. */
  inset: 0;
  z-index: 2;
  pointer-events: none;
}
.ap-hero__inner {
  position: relative;
  /* Above the tree, which is z-index 2. Without this the canopy paints over
     the plate: both are positioned, and the one that declares a z-index wins
     regardless of document order. */
  z-index: 6;
  max-width: var(--ancho-ficha);
  margin: 0 auto;
  padding: 72px 22px 0;
}
.ap-hero__plate {
  background: var(--crema);
  color: var(--noche);
  border-radius: var(--radio-tarjeta);
  padding: 34px 36px 32px;
  max-width: 720px;
  box-shadow: var(--sombra-plato);
}
.ap-hero__title {
  font-family: var(--font-display);
  font-size: clamp(3rem, 7.2vw, 102px);
  line-height: .84;
  letter-spacing: -.025em;
  max-width: 9ch;
  font-weight: 400;
}
.ap-hero__dot { color: var(--ambar); }
.ap-hero__sub {
  margin-top: 16px;
  font-size: clamp(15px, 1.5vw, 19px);
  font-weight: 500;
  max-width: 36ch;
}
.ap-hero__actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 24px; }


/* ---------------------------------------------------------------- directorio */

.ap-directory__head {
  background: var(--crema);
  border-bottom: var(--borde-fuerte);
}
.ap-directory__head-inner {
  max-width: var(--ancho-directorio);
  margin: 0 auto;
  padding: 30px 24px 22px;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(300px, 520px);
  gap: 26px;
  align-items: end;
}
.ap-directory__title {
  font-family: var(--font-display);
  font-size: clamp(2.6rem, 5.4vw, 4.4rem);
  line-height: .86;
  letter-spacing: -.02em;
  font-weight: 400;
}
.ap-directory__body {
  max-width: var(--ancho-directorio);
  margin: 0 auto;
  padding: 24px 24px 70px;
  display: grid;
  grid-template-columns: var(--riel) minmax(0, 1fr);
  gap: 26px;
  align-items: start;
}
.ap-rail {
  position: sticky;
  top: var(--sticky-offset);
  max-height: calc(100vh - 100px);
  overflow-y: auto;
  display: grid;
  gap: 20px;
  padding-right: 6px;
}
.ap-facet__options { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 10px; }
.ap-facet__options--column { display: grid; gap: 7px; }
.ap-facet__options--months { display: grid; grid-template-columns: repeat(4, 1fr); gap: 5px; }
.ap-facet__note { margin-top: 8px; font-size: 12.5px; font-weight: 500; line-height: 1.35; }

/* State rows: a 1px ink gap between them reads as a hairline table. The family
   facet shared these declarations as grouped selectors until the directory
   stopped filtering by family (decision 0040); the state list is the
   treatment's one user now. */
.ap-states { display: grid; gap: 1px; background: rgba(16, 32, 26, .16); margin-top: 10px; }
.ap-state {
  border: 0;
  background: var(--crema);
  color: var(--noche);
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 10px;
  padding: 9px 4px;
  font-family: var(--font-body);
  cursor: pointer;
  text-align: left;
  min-height: 38px;
  text-decoration: none;
}
.ap-state[aria-current] { background: var(--noche); color: var(--ambar); }
.ap-state__name { font-weight: 700; font-size: 14px; }
.ap-state__count {
  font-stretch: var(--label-stretch);
  font-weight: 800;
  font-size: 10px;
  letter-spacing: .1em;
  opacity: .65;
}

.ap-directory__results { display: grid; gap: 18px; }
.ap-directory__chips { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }

/* The directory's pager. Layout only — the controls are `.ap-button--outline`
   and `.ap-label`, which already exist, so no new treatment is introduced and
   the plate/outline exclusivity is somebody else's decision, already made.
   The hairline above is ink at an opacity, like every other rule here. */
.ap-pager {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  align-items: center;
  justify-content: center;
  margin-top: 38px;
  padding-top: 22px;
  border-top: 1px solid var(--linea-suave);
}

.ap-empty { padding: 34px 30px; max-width: 64ch; }
.ap-empty__title {
  font-family: var(--font-display);
  font-size: 32px;
  line-height: 1;
  max-width: 24ch;
  font-weight: 400;
}
.ap-empty__body { margin-top: 14px; font-weight: 500; }
.ap-empty__actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 20px; }

/* The mobile filter trigger; the rail replaces it from 900px up. */
.ap-directory__open { display: none; justify-self: start; }

/* ---------------------------------------------------------------- ficha */

.ap-sheet__head {
  position: relative;
  overflow: hidden;
  background: var(--verde);
  color: var(--crema);
  padding-bottom: 8px;
}
.ap-sheet__crowns {
  position: absolute;
  top: -40px;
  right: -120px;
  width: 760px;
  max-width: 80%;
  opacity: .42;
  pointer-events: none;
}
.ap-sheet__crowns ellipse { animation: viento 9s ease-in-out infinite alternate; transform-origin: center; }
@keyframes viento {
  from { transform: rotate(-.9deg) translateY(0); }
  to { transform: rotate(.9deg) translateY(-6px); }
}
.ap-sheet__head-inner {
  max-width: var(--ancho-ficha);
  margin: 0 auto;
  padding: 26px 22px 0;
  position: relative;
  z-index: 5;
}
.ap-sheet__identity {
  display: grid;
  grid-template-columns: minmax(0, 1.25fr) minmax(300px, .75fr);
  gap: 34px;
  align-items: end;
  padding: 18px 0 34px;
}
.ap-sheet__name {
  font-family: var(--font-display);
  font-size: var(--display-h1);
  line-height: var(--display-line);
  letter-spacing: -.02em;
  color: var(--crema);
  font-weight: 400;
}
.ap-sheet__bin {
  margin-top: 10px;
  font-style: italic;
  font-weight: 500;
  font-size: clamp(1.15rem, 2.6vw, 1.7rem);
  line-height: 1.1;
}
/* The standfirst: the archive's own sentence about the tree (decision 0054).
   Sized between the binomial above it and the body below, so it reads as an
   opening rather than as another field. `max-width` in ch rather than a
   percentage: it is a measure, and the header is a grid column that changes
   width with the photograph beside it. */
.ap-sheet__line {
  margin-top: 14px;
  max-width: 46ch;
  font-size: clamp(1rem, 1.6vw, 1.15rem);
  line-height: 1.45;
  font-weight: 500;
  /* Cream at .85 on --verde is the floor the contrast rule sets; this sits on
     the dark head, so it stays at .92 rather than being dimmed to a caption. */
  opacity: .92;
}

.ap-names {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 10px;
  margin-top: 22px;
  max-width: 58ch;
}
.ap-name {
  display: flex;
  align-items: baseline;
  gap: 7px;
  border: 2px solid var(--crema-borde);
  border-radius: var(--radio-pill);
  padding: 5px 13px 6px;
}
.ap-name__text { font-weight: 700; font-size: 14.5px; }
/* The same pill on a cream ground — «El proyecto»'s example card — where the
   ink is night, so the border is the resting pill's. */
.ap-name--ink { border-color: var(--borde-pill); }
.ap-sheet__provenance {
  margin-top: 12px;
  font-size: 13px;
  font-weight: 500;
  opacity: .88;
  max-width: 56ch;
}

/* --verde is the lightest ground the archive puts cream text on: 5.82:1 at full
   opacity, so anything faded below .85 falls under AA. --selva has enough
   headroom that the usual .7 is fine there. axe caught this on the sheet header. */
.ap-sheet__head .ap-label--muted,
.ap-sheet__head .ap-authority,
.ap-sheet__head .ap-breadcrumbs { opacity: .88; }

.ap-sheet__body {
  max-width: var(--ancho-ficha);
  margin: 0 auto;
  padding: 34px 22px 70px;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(300px, 340px);
  gap: 34px;
  align-items: start;
}
.ap-sheet__main { display: grid; gap: 26px; }
.ap-sheet__aside {
  display: grid;
  gap: 20px;
  position: sticky;
  top: var(--sticky-offset);
}
.ap-sheet__section > .ap-prose { margin-top: 14px; }

/* The recorded states as the choropleth the Árbol del día draws
   (web/choropleth.py). Same treatment as `.ap-slide-range__state` in
   laminas.css, on the same crema ground: presence in fixed --verde — the
   species pairs are verified against their own ink, not against crema —
   absence ink at 10 %, cream strokes. Only the wrapper sizing differs: the
   sheet's map fills its column where the slide fills a fixed frame.
   test_palette.py holds shaded against unshaded at 3:1 or better on both. */
.ap-choropleth { margin-top: 16px; }
.ap-choropleth__head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
}
.ap-choropleth svg { width: 100%; height: auto; display: block; margin-top: 10px; }
.ap-choropleth__state {
  fill: color-mix(in srgb, var(--noche) 10%, transparent);
  stroke: var(--crema);
  stroke-width: 1;
}
.ap-choropleth__state--on { fill: var(--verde); }
.ap-choropleth__legend {
  margin: 10px 0 0;
  display: flex;
  flex-wrap: wrap;
  gap: 7px 18px;
}
.ap-choropleth__key { display: inline-flex; align-items: center; gap: 7px; }
.ap-choropleth__swatch {
  width: 12px;
  height: 12px;
  border-radius: 4px;
  background: color-mix(in srgb, var(--noche) 10%, transparent);
}
.ap-choropleth__swatch--on { background: var(--verde); }
.ap-choropleth__note {
  margin-top: 10px;
  font-size: 13px;
  font-weight: 500;
  line-height: 1.4;
  max-width: 56ch;
}
.ap-calendar__row {
  display: grid;
  grid-template-columns: 88px 1fr;
  gap: 8px;
  align-items: center;
  margin-top: 8px;
}
.ap-calendar__text {
  margin-top: 12px;
  min-height: 3.4em;
  font-weight: 500;
  font-size: 14.5px;
  line-height: 1.4;
}
.ap-calendar__foot {
  margin-top: 10px;
  font-size: 13px;
  font-weight: 500;
  line-height: 1.4;
  border-top: 2px solid var(--linea-suave);
  padding-top: 9px;
}

/* The hero photograph, in the house arc — the silueta card's old slot, before
   decision 0051 sent that to the sidebar. `999px 999px` clamps the top to a
   true semicircle at any width, the same recipe as the directory card's window
   and the IG cover. Plate treatment: the selva fill behind the photograph plus
   the house shadow, and no outline anywhere on it. */
.ap-sheet__hero-photo {
  margin: 0;
  width: 100%;
  max-width: 470px;
}
.ap-sheet__hero-window {
  position: relative;
  display: block;
  aspect-ratio: 1 / 1;
  border-radius: 999px 999px 16px 16px;
  overflow: hidden;
  background: var(--selva);
  box-shadow: var(--sombra-tarjeta);
}
.ap-sheet__hero-window img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* The phone hero's gradient; in the arc the photograph needs no fade. */
.ap-sheet__hero-fade { display: none; }
.ap-sheet__hero-credit { display: block; margin-top: 10px; }

/* The vitals row under the title: each value is the same model property the
   «Clasificación» card prints, so the two agree by construction. The rule is
   crema at .35 because this sits on --verde; the labels lean on the head's own
   .88 opacity clamp above. */
.ap-sheet__facts {
  display: flex;
  flex-wrap: wrap;
  gap: 26px;
  margin-top: 26px;
  border-top: 2px solid var(--crema-linea);
  padding-top: 14px;
}
.ap-sheet__fact { min-width: 0; }
.ap-sheet__fact-value {
  display: block;
  margin-top: 2px;
  font-weight: 700;
  font-size: 17px;
}
/* Both classes, or the cascade hands the mono placeholder the value's 17px 700.
   Opacity .88, not .ap-pending's .7: cream under .85 on --verde fails AA. */
.ap-sheet__fact-value.ap-pending {
  font-size: 13px;
  font-weight: 500;
  line-height: 1.6;
  opacity: .88;
}
.ap-sheet__fact--phone { display: none; }

/* A sheet with no photograph keeps a text-only hero: one column, no figure and
   no placeholder — the absence is stated where the photographs live. */
.ap-sheet__identity--text-only { grid-template-columns: minmax(0, 1fr); }

/* ---------------------------------------------------------------- proyecto */

/* «El proyecto» is laid out as the archive's own sheet (decision 0058): it
   wears `.ap-sheet__head`, `.ap-sheet__facts` and the «Clasificación» card's
   `.ap-taxon` rows, so the two pages share their styling. The markup of the
   example card is a copy of the sheet's rows, and nothing holds the two
   together beyond the classes — a change to a row there is a change to make
   here by hand. What is here is only what the sheet does not have: the
   display figures, the process steps, the reading chart, the rules and the
   closing line. */

.ap-about { padding-top: 48px; padding-bottom: 70px; }
.ap-about__stamp { display: block; margin-top: 16px; }
.ap-about__section { margin-top: 70px; }

/* Prose beside a card: the sheet's own 1fr / 340px split. */
.ap-about__split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(300px, 340px);
  gap: 34px;
  align-items: start;
}
.ap-about__split--chart { margin-top: 48px; }
/* No example card, no second track: the prose would otherwise sit beside an
   empty 340px gutter, which is the state of a fresh database. */
.ap-about__split--alone { grid-template-columns: minmax(0, 1fr); }

.ap-figure__number {
  font-family: var(--font-display);
  font-size: 46px;
  line-height: .9;
  display: block;
  margin-top: 6px;
  font-weight: 400;
}

/* The example card's own three lines; the rows are `.ap-taxon`. */
.ap-about__card-note { margin-top: 10px; font-size: 13px; line-height: 1.4; font-weight: 500; }
.ap-about__card-foot {
  margin-top: 12px;
  padding-top: 10px;
  border-top: 2px solid var(--linea-suave);
  font-family: var(--font-mono);
  font-size: 12px;
  opacity: .7;
}
.ap-about__band-note { display: block; margin-top: 6px; font-size: 13px; font-weight: 500; }
/* `.ap-authority` is .62em of its parent, sized for the display title on the
   sheet header; inside a 15px value that is 9px. 12px keeps it subordinate to
   the binomial and above the smallest label the system prints. */
.ap-taxon__value .ap-authority { font-size: 12px; }
.ap-names--card { margin-top: 0; max-width: none; }
.ap-taxon--top { align-items: start; }
.ap-taxon--last { border-bottom: 0; }

/* The five steps: a rule, a display numeral, a label, a short paragraph. */
.ap-about__steps {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 22px;
  margin-top: 34px;
}
.ap-step { border-top: var(--borde-fuerte); padding-top: 14px; }
.ap-step__number {
  display: block;
  font-family: var(--font-display);
  font-size: 46px;
  line-height: .9;
  font-weight: 400;
}
.ap-step__title { display: block; margin-top: 12px; }
.ap-step__body { margin-top: 8px; font-size: 14.5px; line-height: 1.42; font-weight: 500; }

/* The reading chart: one bar per work. The track is ink at an opacity and the
   fill is fixed --verde, the same single hue the map and the scale use; a
   14px bar with 4px ends is the `.ap-band__cell` the sheet already draws. An
   unread work keeps its empty track, which is the flowering band's rule —
   the absence is drawn where it is. */
.ap-about__note { margin-top: 10px; font-size: 13px; line-height: 1.4; font-weight: 500; max-width: 56ch; }
.ap-reading { margin-top: 14px; }
.ap-reading__row {
  display: grid;
  gap: 8px;
  padding: 12px 0 13px;
  border-top: 1px solid var(--linea-suave);
}
.ap-reading__head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 18px;
}
.ap-reading__title { font-weight: 700; font-size: 14px; line-height: 1.3; }
/* The count never wraps: «297 /» over «316» read as two numbers at 390px. */
.ap-reading__count { white-space: nowrap; flex-shrink: 0; }
.ap-reading__track {
  height: 14px;
  border-radius: 4px;
  background: var(--relleno-suave);
  overflow: hidden;
}
.ap-reading__fill { height: 14px; border-radius: 4px; background: var(--verde); }
.ap-reading__total { border-top: var(--borde-fuerte); padding-top: 12px; }

/* The authority ladder, on the dark plate. Numerals in ámbar, like the
   pressed state everywhere else; hairlines are cream at .35. */
.ap-rank { margin-top: 8px; }
.ap-rank__row {
  display: grid;
  grid-template-columns: 30px minmax(0, 1fr);
  gap: 10px;
  align-items: baseline;
  padding: 8px 0;
  border-bottom: 1px solid var(--crema-linea);
}
.ap-rank__row:last-child { border-bottom: 0; }
.ap-rank__number {
  font-family: var(--font-display);
  font-size: 22px;
  line-height: 1;
  color: var(--ambar);
  font-weight: 400;
}
.ap-rank__title { font-weight: 600; font-size: 14.5px; line-height: 1.3; }
/* Cream at .92 on --selva: the plate is darker than --verde, so the .85 floor
   the sheet header notes has room to spare here. */
.ap-about__dark-note { margin-top: 14px; font-size: 14px; line-height: 1.45; font-weight: 500; opacity: .92; }
.ap-about__dark-note + .ap-about__dark-note { margin-top: 12px; }

/* Three rules, three outline cards. */
.ap-about__rules {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 18px;
  margin-top: 22px;
}
.ap-rule { padding: 18px 20px 20px; }
.ap-rule__title {
  font-family: var(--font-display);
  font-size: 26px;
  line-height: .98;
  letter-spacing: -.01em;
  margin-top: 10px;
  font-weight: 400;
}
.ap-rule__body { margin-top: 12px; font-size: 14.5px; line-height: 1.45; font-weight: 500; }

/* The closing line, in the portada's own words, with its ámbar full stop. */
.ap-about__goal {
  margin-top: 70px;
  border-top: var(--borde-fuerte);
  padding-top: 34px;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 34px;
  align-items: start;
}
.ap-about__goal-title {
  margin-top: 12px;
  font-family: var(--font-display);
  font-size: clamp(44px, 4.5vw, 64px);
  line-height: .86;
  letter-spacing: -.02em;
  max-width: 9ch;
  font-weight: 400;
}
.ap-about__goal-body { padding-top: 30px; }
.ap-about__actions { margin-top: 24px; }

/* ---------------------------------------------------------------- panel de filtros (movil) */

.ap-panel { display: none; }
.ap-panel__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 16px;
  background: var(--noche);
  color: var(--crema);
}
.ap-panel__title {
  font-stretch: var(--title-stretch);
  font-weight: 900;
  font-size: 19px;
  text-transform: uppercase;
  letter-spacing: -.01em;
}
.ap-panel__close {
  border: 2px solid var(--crema-borde);
  background: transparent;
  color: var(--crema);
  border-radius: var(--radio-pill);
  width: 44px;
  height: 44px;
  font-size: 19px;
  line-height: 1;
  cursor: pointer;
}
.ap-panel__body { flex: 1; overflow-y: auto; padding: 16px 16px 20px; display: grid; gap: 20px; }
.ap-panel__foot {
  display: flex;
  gap: 9px;
  padding: 12px 16px 16px;
  border-top: var(--borde-fuerte);
  background: var(--crema);
}
.ap-panel__foot .ap-button { min-height: 46px; justify-content: center; }
.ap-panel__foot .ap-button--amber { flex: 1; }

/* ---------------------------------------------------------------- responsive */

@media (max-width: 1080px) {
  /* One column — and «Fuentes» closes the page. `display: contents` lifts the
     main column's sections into the body grid so `order` can move the one
     late-comer past the aside's cards; everything else keeps source order at
     order 0. The body gap drops to the 26px the main column used, since that
     grid now spaces every section. The aside holds no links, so the visual
     reorder moves no focus stop. */
  .ap-sheet__body { grid-template-columns: minmax(0, 1fr); gap: 26px; }
  .ap-sheet__main { display: contents; }
  .ap-sheet__body .ap-sources { order: 1; }
  .ap-sheet__aside { position: static; }
  .ap-sheet__identity { grid-template-columns: minmax(0, 1fr); gap: 24px; }

  .ap-about__split { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 900px) {
  .ap-directory__body { grid-template-columns: minmax(0, 1fr); }
  .ap-directory__head-inner { grid-template-columns: minmax(0, 1fr); }

  /* The rail becomes a full-screen sheet, opened by a 44px pill. */
  .ap-directory__open { display: inline-flex; }
  .ap-rail { display: none; }

  .ap-panel {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 90;
    background: var(--crema);
    flex-direction: column;
  }
  .ap-panel:target,
  .ap-panel.is-open { display: flex; }
  .ap-panel .ap-rail { display: grid; position: static; max-height: none; overflow: visible; }

  /* The steps stack, numeral beside the text; the rules and the closing line
     go to one column. */
  .ap-about__steps { grid-template-columns: minmax(0, 1fr); gap: 20px; }
  .ap-step {
    display: grid;
    grid-template-columns: 56px minmax(0, 1fr);
    gap: 0 14px;
  }
  .ap-step__number { grid-row: 1 / span 2; }
  .ap-step__title { margin-top: 4px; }
  .ap-about__rules { grid-template-columns: minmax(0, 1fr); }
  .ap-about__goal { grid-template-columns: minmax(0, 1fr); }
  .ap-about__goal-body { padding-top: 0; }
}

@media (max-width: 620px) {
  :root { --canal: 16px; }

  .ap-nav__inner { padding: 0 16px; }
  .ap-nav__links { gap: 14px; }
  .ap-nav__link { font-size: 11.5px; }

  .ap-hero__inner { padding: 40px 16px 0; }
  .ap-hero__plate { padding: 24px 20px 22px; }

  .ap-sheet__head-inner,
  .ap-sheet__body { padding-inline: 16px; }
  .ap-directory__head-inner,
  .ap-directory__body { padding-inline: 16px; }

  .ap-grid { grid-template-columns: minmax(0, 1fr); }

  .ap-nav__links { display: none; }
  .ap-nav__menu { display: block; }

  /* Mobile species card: text | 88px photo, bloom bar spanning beneath.
     Placement is explicit because the photo comes first in source order — it has
     to, for the absolutely-positioned desktop treatment. */
  .ap-species {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 88px;
    grid-template-rows: auto auto auto;
    gap: 12px;
    min-height: 0;
    padding: 15px;
    box-shadow: var(--sombra-movil);
  }
  .ap-species:hover { transform: none; }
  .ap-species__text { grid-column: 1; grid-row: 1 / span 2; }
  .ap-species__photo { grid-column: 2; grid-row: 1; }
  .ap-species__side { grid-column: 2; grid-row: 2; margin-top: 0; gap: 7px; }
  .ap-species__foot { grid-column: 1 / -1; grid-row: 3; }
  .ap-species__photo {
    /* `relative`, not `static`. It only needs to leave the desktop layout's
       absolute corner and sit in the grid, and `relative` does that — while
       staying the containing block for the image inside it, which is absolutely
       positioned so it can fill the arch.

       As `static` it was not a containing block, so the image resolved against
       `.ap-species` instead and `inset: 0` stretched it over the entire card:
       photograph full-bleed, name and family and height and bloom bar all
       painted underneath it and invisible. At 390px, on the directory. */
    position: relative;
    width: auto;
    height: 78px;
    aspect-ratio: auto;
    /* No padding: the image fills the window here too. The placeholder carries
       its own. */
    border-radius: 999px 999px 12px 12px;
  }
  .ap-species__name,
  .ap-species__bin,
  .ap-species__labels { max-width: none; }
  .ap-species__measure { margin-top: 0; padding-top: 0; max-width: none; }
  .ap-species__height { font-size: 21px; text-align: right; }
  .ap-species__foot {
    gap: 5px;
    border-top: 1.5px solid currentColor;
    padding-top: 9px;
  }
  .ap-species__months { max-width: none; margin-top: 0; }
  .ap-species__month { height: 8px; }
  .ap-species__note { max-width: none; }
  .ap-species__close-row {
    display: flex;
    justify-content: space-between;
    gap: 10px;
    font-stretch: var(--label-stretch);
    font-weight: 800;
    font-size: 9px;
    letter-spacing: .11em;
    text-transform: uppercase;
    line-height: 1.3;
  }

  .ap-footer__inner { grid-template-columns: minmax(0, 1fr); gap: 26px; padding: 34px 16px 30px; }

  /* «El proyecto»: the figures ride the sheet's 2×2 strip below, a size down. */
  .ap-about { padding-top: 40px; padding-bottom: 56px; }
  .ap-about__section,
  .ap-about__goal { margin-top: 56px; }
  .ap-sheet__facts .ap-figure__number { font-size: 40px; }

  /* The vitals become the phone's 2×2 strip, and «Estado» joins them: the
     card that states it is a long scroll away here. Hairlines are crema at
     .35, the head's own line. */
  .ap-sheet__facts {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0;
    padding-top: 0;
  }
  .ap-sheet__fact { padding: 13px 0 14px; }
  .ap-sheet__fact:nth-child(odd) { padding-right: 16px; }
  .ap-sheet__fact:nth-child(even) {
    padding-left: 16px;
    border-left: 1px solid var(--crema-linea);
  }
  .ap-sheet__fact:nth-child(n+3) { border-top: 1px solid var(--crema-linea); }
  .ap-sheet__fact--phone { display: block; }
  .ap-sheet__fact-value { margin-top: 3px; font-size: 16px; }

  /* The phone hero, when the sheet has a photograph: the picture bleeds to the
     edges on --selva with the title over its foot — 390px is where the
     photographs have the resolution to bleed; the arc carries every wider
     screen (decision 0051). One <img>, restyled: the fade and the overlay are
     the only elements the arc does not use. */
  .ap-sheet__head--photo { background: var(--selva); padding-bottom: 0; }
  .ap-sheet__head--photo .ap-sheet__head-inner { padding: 0; }
  /* The breadcrumb becomes a pill over the photograph. Selva at 92%, not the
     prototype's 80%: over a white sky the 80% ground measures ~3,3:1 under
     cream and fails AA, while 92% keeps ~5,5:1 — the principle beats the
     example, as decision 0008 already ruled. */
  .ap-sheet__head--photo .ap-breadcrumbs {
    position: absolute;
    top: 14px;
    left: 16px;
    z-index: 3;
    background: color-mix(in srgb, var(--selva) 92%, transparent);
    border-radius: var(--radio-pill);
    padding: 5px 11px;
    opacity: 1;
  }
  /* The binomial crumb (and its slash) leaves the pill: the title cluster
     prints it fifty pixels below, over the same photograph. */
  .ap-sheet__head--photo .ap-breadcrumbs > span:nth-last-child(-n+2) { display: none; }
  /* One single-column grid holds the whole hero. `display: contents` lifts the
     lede's children and the figure's two boxes into it — the trick
     `.ap-sheet__main` already plays at ≤1080px — so the window and the title
     cluster can share row one. The row is as tall as the taller of the two:
     440px of photograph, or the cluster on a sheet with many name pills. A
     fixed 440px overlay clipped «Caoba»'s title behind the nav — eleven pills
     are taller than the photograph. */
  .ap-sheet__head--photo .ap-sheet__identity {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 0;
    padding: 0;
  }
  .ap-sheet__head--photo .ap-sheet__lede,
  .ap-sheet__head--photo .ap-sheet__hero-photo { display: contents; }
  .ap-sheet__head--photo .ap-sheet__hero-window {
    grid-area: 1 / 1;
    aspect-ratio: auto;
    min-height: 440px;
    border-radius: 0;
    box-shadow: none;
  }
  .ap-sheet__head--photo .ap-sheet__hero-fade {
    display: block;
    position: absolute;
    inset: 0;
    pointer-events: none;
    background: linear-gradient(
      to top,
      color-mix(in srgb, var(--selva) 95%, transparent) 0%,
      color-mix(in srgb, var(--selva) 85%, transparent) 30%,
      color-mix(in srgb, var(--selva) 40%, transparent) 62%,
      color-mix(in srgb, var(--selva) 8%, transparent) 84%
    );
  }
  /* The same cell as the window, bottom-aligned over it. Every tap passes
     through — the window underneath is the link — and the top padding keeps a
     grown cluster clear of the breadcrumb pill.

     The cluster carries its own wash, in *pixel* stops: the photograph's fade
     below is proportional to the photo, so on a sheet whose pills outgrow it
     the title would sit at the fade's weak end. Anchored to the cluster's own
     box, the top 92px (the padding) fades in and everything that carries text
     sits on ≥88% selva — ~4,8:1 under cream even over a white sky, which is
     what the proportional stops could not promise. */
  .ap-sheet__head--photo .ap-sheet__title-cluster {
    grid-area: 1 / 1;
    align-self: end;
    z-index: 2;
    padding: 92px 16px 20px;
    pointer-events: none;
    background: linear-gradient(
      to bottom,
      transparent 0,
      color-mix(in srgb, var(--selva) 55%, transparent) 60px,
      color-mix(in srgb, var(--selva) 88%, transparent) 130px,
      color-mix(in srgb, var(--selva) 92%, transparent) 100%
    );
  }
  .ap-sheet__head--photo .ap-names {
    margin-top: 14px;
    gap: 7px;
    max-width: none;
  }
  /* Everything below the photograph auto-places into the grid's later rows;
     `order` sets the reading sequence — credit band, data strip, then an
     editor's note about the names, which does not belong over a picture. */
  .ap-sheet__head--photo .ap-sheet__hero-credit {
    order: -2;
    margin: 0;
    padding: 9px 16px 10px;
    border-bottom: 1px solid var(--crema-linea);
  }
  .ap-sheet__head--photo .ap-sheet__facts {
    order: -1;
    margin-top: 0;
    border-top: 0;
  }
  .ap-sheet__head--photo .ap-sheet__fact { padding: 13px 16px 14px; }
  .ap-sheet__head--photo .ap-sheet__provenance {
    margin: 0;
    padding: 14px 16px 18px;
    max-width: none;
  }
}

/* Touch targets never fall below 44px on a phone. */
@media (pointer: coarse) {
  .ap-button,
  .ap-pill,
  .ap-chip { min-height: 44px; }
  .ap-pill--square { min-height: 44px; }
}
