/* ============================================================================
   Media layouts. Three, and only three.

   Every photograph on every site in this house goes into one of these. That is
   the point: a page that invents a fourth arrangement is the reason a set of
   sites stops looking like one set of sites.

     .media-banner   one wide photograph, edge to edge, with an optional
                     caption bar. Section opener or page hero.
     .media-grid     two or three photographs abreast, each with a caption.
                     A set of things, browsed.
     .media-figure   one photograph inside a column of prose, at text width
                     or pulled slightly wider. Evidence for the sentence
                     beside it.

   Markup for each is in packages/core/MEDIA-LAYOUTS.md. Read that before
   using these; the class names alone will not tell you where the width and
   height attributes go, and those attributes are the whole reason nothing on
   these pages shifts while it loads.

   WHAT IS ENCODED HERE SO A PAGE CANNOT GET IT WRONG

   - aspect-ratio on every frame. The box is the right shape before the bytes
     arrive, so the text below it never jumps. The img fills the frame with
     object-fit: cover and is centred.
   - The img is display:block and width:100%, so the browser's own intrinsic
     ratio from the width and height attributes never fights the frame.
   - background on the frame, so the space is a considered grey rather than a
     white hole while the image decodes.
   - No hover transform on the image itself, only on its frame's inner scale,
     and it is driven by --dur-3, which the token layer zeroes under
     prefers-reduced-motion.
   - Captions use the caption ramp from tokens.css and sit outside the frame,
     never over the photograph. Text on a photograph is the single most
     reliable way to look cheap.

   Sizes and spacing come from tokens.css. Nothing here invents a value.
   ========================================================================= */

@layer components {

  /* ------------------------------------------------------------------ */
  /* shared                                                             */
  /* ------------------------------------------------------------------ */

  .media-frame {
    position: relative;
    overflow: hidden;
    display: block;
    background: var(--surface-2);
    border: 1px solid var(--rule);
    aspect-ratio: var(--media-ratio, 3 / 2);
  }

  .media-frame > img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: var(--media-focus, 50% 50%);
    /* The image is the only saturated thing on a page of cool paper. Let it
       arrive rather than appear. */
    transition: transform var(--dur-3) var(--ease-out);
  }

  .media-cap {
    margin: var(--sp-3) 0 0;
    font-size: var(--t-caption);
    line-height: var(--lh-caption);
    letter-spacing: var(--tr-caption);
    color: var(--ink-3);
  }

  /* The photographer line. Small, permanent, never a footnote marker: a
     credit is a courtesy, not a citation, and the check script must not
     confuse the two. */
  .media-credit {
    display: block;
    margin-top: var(--sp-1);
    font-family: var(--font-mono, inherit);
    font-size: var(--t-micro);
    line-height: var(--lh-micro);
    letter-spacing: var(--tr-micro);
    text-transform: uppercase;
    color: var(--ink-3);
  }
  .media-credit a { color: inherit; text-decoration: none; border-bottom: 1px solid var(--rule) }
  .media-credit a:hover { color: var(--ink); border-bottom-color: currentColor }

  /* A media layout dropped straight into the house 12 column .grid spans
     the whole of it. Without this every one of them would sit in column 1
     and be one twelfth of the page wide, which is the first thing anyone
     using these gets wrong. */
  .grid > .media-banner,
  .grid > .media-grid,
  .grid > .media-figure,
  .grid > .compare,
  .grid > .media-note { grid-column: 1 / -1 }

  /* ------------------------------------------------------------------ */
  /* 1. banner                                                          */
  /*    One photograph across the full content width, or across the      */
  /*    full viewport with .media-banner--bleed. Ratio steps from 4:3 on */
  /*    a phone to 3:1 on a desktop, because a 3:1 crop of anything is   */
  /*    unreadable at 390px wide.                                        */
  /* ------------------------------------------------------------------ */

  .media-banner {
    margin: var(--sp-8) 0;
  }
  .media-banner > .media-frame { --media-ratio: 4 / 3 }

  @media (min-width: 640px)  { .media-banner > .media-frame { --media-ratio: 16 / 9 } }
  @media (min-width: 1024px) { .media-banner > .media-frame { --media-ratio: 3 / 1 } }

  .media-banner--bleed {
    width: 100vw;
    max-width: var(--max-bleed);
    margin-inline: calc(50% - 50vw);
  }
  @media (min-width: 1680px) {
    .media-banner--bleed { margin-inline: auto }
  }
  .media-banner--bleed > .media-frame { border-inline: 0 }

  /* The caption bar under a banner is a rule plus two facts, left and right,
     which collapses to stacked on a phone. */
  .media-banner > .media-cap {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sp-2) var(--sp-5);
    justify-content: space-between;
    padding-top: var(--sp-3);
    margin-top: 0;
    border-top: 1px solid var(--rule);
  }
  .media-banner > .media-cap > * { margin: 0 }
  .media-banner > .media-cap .media-credit { margin-top: 0 }

  /* ------------------------------------------------------------------ */
  /* 2. grid                                                            */
  /*    auto-fill rather than a fixed column count, so the same markup   */
  /*    is 1 up on a phone, 2 up on a tablet and 3 up on a desktop with  */
  /*    no media query and no orphan row of one.                         */
  /* ------------------------------------------------------------------ */

  .media-grid {
    display: grid;
    gap: var(--sp-6) var(--gutter);
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 280px), 1fr));
    margin: var(--sp-7) 0;
    padding: 0;
    list-style: none;
  }

  /* Two up rather than three, for images that need the extra width. */
  .media-grid--wide {
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 420px), 1fr));
  }

  .media-grid > li, .media-grid > figure { margin: 0 }
  .media-grid .media-frame { --media-ratio: 3 / 2 }
  .media-grid .media-frame--portrait { --media-ratio: 3 / 4 }
  .media-grid .media-frame--square   { --media-ratio: 1 / 1 }

  /* A grid cell that is a link gets the one hover in this file. 1.02 is
     deliberately almost nothing: it should register as alive, not as motion. */
  a.media-frame:hover > img,
  .media-grid a:hover .media-frame > img { transform: scale(1.02) }

  .media-grid h3 {
    margin: var(--sp-4) 0 0;
    font-size: var(--t-h4);
    line-height: var(--lh-h4);
    letter-spacing: var(--tr-h4);
  }

  /* ------------------------------------------------------------------ */
  /* 3. figure                                                          */
  /*    Inside prose. Default is the text measure, so the photograph is  */
  /*    the same width as the sentence that introduces it. --wide pulls  */
  /*    it out of the measure on a desktop, which is the only time a     */
  /*    figure should ever exceed the column.                            */
  /* ------------------------------------------------------------------ */

  .media-figure {
    margin: var(--sp-7) 0;
    max-width: var(--max-text);
  }
  .media-figure > .media-frame { --media-ratio: 3 / 2 }
  .media-figure--portrait > .media-frame { --media-ratio: 3 / 4; max-width: 32rem }

  @media (min-width: 1024px) {
    .media-figure--wide { max-width: calc(var(--max-text) + var(--sp-11)) }
  }

  .media-figure > figcaption {
    margin: var(--sp-3) 0 0;
    padding-left: var(--sp-4);
    border-left: 2px solid var(--rule);
    font-size: var(--t-caption);
    line-height: var(--lh-caption);
    letter-spacing: var(--tr-caption);
    color: var(--ink-2);
  }
  .media-figure > figcaption .media-credit { margin-top: var(--sp-2) }

  /* ------------------------------------------------------------------ */
  /* the standing note that photographs are contributed                 */
  /* ------------------------------------------------------------------ */

  .media-note {
    margin: var(--sp-6) 0 0;
    font-size: var(--t-sm);
    line-height: var(--lh-sm);
    letter-spacing: var(--tr-sm);
    color: var(--ink-3);
  }
  .media-note a { color: var(--accent) }

  /* ------------------------------------------------------------------ */
  /* print: a photograph that breaks across a page is worse than none   */
  /* ------------------------------------------------------------------ */

  @media print {
    .media-figure, .media-banner, .media-grid > li, .media-grid > figure {
      break-inside: avoid;
    }
    .media-frame > img { transition: none }
  }
}

@layer components {
  /* ------------------------------------------------------------------
     A frame with no photograph in it yet.

     The label is "Photograph on the way", not "Image coming soon". Coming
     soon is a stock phrase and it apologises; VOICE.md bans the apologetic
     register and CLAUDE.md 1.7 says lead with what will exist rather than
     what is missing. On the way is the same fact, said by a person who has
     it in hand.

     It is a dashed frame, deliberately not a gradient: a gradient reads as
     a design choice and quietly becomes permanent, which is exactly how
     this site ended up with 56 of them.

     Every placeholder carries data-want describing the photograph needed,
     so the frame says what will fill it and a later pass can search on it.
     ------------------------------------------------------------------ */
  .ph-box {
    position: relative;
    aspect-ratio: var(--ph-ratio, 3 / 2);
    display: grid;
    place-items: center;
    gap: var(--sp-2);
    padding: var(--sp-5);
    text-align: center;
    border: 1px dashed color-mix(in oklab, var(--ink-3) 45%, transparent);
    border-radius: 4px;
    background: var(--surface-2);
  }
  .ph-box::before {
    content: "Photograph on the way";
    font-family: var(--font-mono, inherit);
    font-size: var(--t-caption);
    letter-spacing: 0.10em;
    text-transform: uppercase;
    color: var(--ink-3);
  }
  /* the subject, so the frame is legible as a plan rather than a gap */
  .ph[data-want] .ph-box::after {
    content: attr(data-want);
    max-width: 32ch;
    font-size: var(--t-sm);
    line-height: var(--lh-sm);
    color: var(--ink-4, var(--ink-3));
  }
  /* the caption is already written, so only the picture is missing */
  .ph figcaption { color: var(--ink-3) }

  @media (prefers-reduced-motion: no-preference) {
    .ph-box { transition: border-color 150ms ease }
  }
}

@layer components {
  /* ------------------------------------------------------------------
     Two photographs side by side, for a difference you can only see by
     comparing.

     Shoaling against schooling is the case this was built for: neither
     photograph means much alone, and the moment they sit together the
     difference is obvious without a word of explanation. Same idea for
     six fish against twelve, or a group before and after a startle.

     Side by side on anything wider than a phone, stacked below that,
     because two 40 percent-wide photographs are worse than one at full
     width. The pair keeps a shared caption so the comparison is stated
     once rather than split across two captions that nobody reads together.
     ------------------------------------------------------------------ */
  .compare {
    display: grid;
    gap: var(--sp-4);
    margin-block: var(--sp-7);
  }
  @media (min-width: 700px) {
    .compare-pair { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-4) }
  }
  .compare-pair { display: grid; gap: var(--sp-4) }
  .compare figure { margin: 0 }
  .compare img,
  .compare .ph-box {
    width: 100%;
    height: auto;
    aspect-ratio: var(--compare-ratio, 4 / 3);
    object-fit: cover;
    border-radius: 3px;
    display: block;
  }
  /* the label sits ON the image, so the eye never loses which is which */
  .compare figure { position: relative }
  .compare figure > figcaption {
    margin-top: var(--sp-2);
    font-size: var(--t-sm);
    line-height: var(--lh-sm);
    color: var(--ink-2);
  }
  .compare figcaption b { color: var(--ink) }
  /* one sentence under the pair, naming what to look for */
  .compare > .compare-note {
    max-width: var(--max-text);
    font-size: var(--t-sm);
    line-height: var(--lh-sm);
    color: var(--ink-3);
    margin: 0;
  }
}
