/* ---------------------------------------------------------------
   Home page - the "latest release / announcement" panel.

   Geometry is fitted to references/home-page-mockup-desktop.png,
   measured at a 1920x875 stage (the art is 2.194:1):

     panel      x 608..1437, y 210..733   -> 830 x 524 (the mockup's
                corners are square; the build rounds them - see below)
     heading    glyph top y 239, centred on the panel
     cover      x 628..929, y 278..580    -> 302 square
     gutter     11px
     text box   x 941..1414, y 278..707   -> 474 x 430
     buttons    2 rows of 4, 50px circles, 15.3 / 9 gaps, centred
                under the cover, bottom row ending level with the box

   Everything below is expressed in em against the panel's own font
   size, which is itself a fraction of the panel width - so the whole
   block scales as one piece and the internal proportions hold at any
   viewport. 1em = 26px at the reference width.
   --------------------------------------------------------------- */

.feature {
  /* 830/1920 of the stage, capped so a short viewport cannot overflow it */
  --pw: min(43.229vw, 95.04vh);

  position: fixed;
  left: 53.26%;                       /* the panel sits right of centre */
  top: 53.89%;
  transform: translate(-50%, -50%);
  z-index: 20;

  width: var(--pw);
  height: calc(var(--pw) * 0.63133);  /* 524/830 */
  /* 6px less on every side than the mockup's padding: the border below is
     drawn inside the box, and the contents should not move because of it. */
  padding: 0.90385em 0.65385em 0.76923em 0.53846em;

  display: flex;
  flex-direction: column;

  font-family: var(--font-display);
  font-size: calc(var(--pw) * 0.0313253);   /* 26px at the reference width */

  /* ---- the four editable colours ----
     Every one is a var() with the mockup-fitted value as its FALLBACK, so
     this file stays the single source of truth for the default look. A post
     that overrides one carries `--panel-bg` (etc.) in a style attribute on
     this element, written in src/routes/public.js from values that have
     already been validated as colour literals. Nothing else in the panel
     names a colour for these surfaces - change them here, not there.

     The defaults are translucent on purpose: the panel sits on the
     illustration and the mockup lets it through. The editor's colour rows
     each carry an opacity slider for the same reason. */
  background: var(--panel-bg, rgba(229, 194, 243, .78));

  /* The corner radius of the two inner boxes - the cover and the copy - kept
     as one token so they can never drift apart. It is a fraction of the PANEL
     width, not an em: an em resolves against the box's own font-size, and
     .feature__media--empty sets its own (23.7px for the "no cover" label), so
     the same `0.38462em` came out 10px on a filled box and 9.1px on an empty
     one. Measured that way before it was noticed. 10px at the 830px
     reference width. */
  --box-radius: calc(var(--pw) * 0.012048);

  /* Owner's request, 2026-08-24. The mockup draws this panel with square
     corners and no border - both of these are deliberate departures from it. */
  border: 6px solid var(--panel-border, rgba(194, 148, 203, .94));
                                               /* default: halfway between the
                                                  panel fill and the inner boxes */
  border-radius: var(--radius-md);             /* 14px */
}

/* Poppins 600 at 23px renders 360px wide with a 22px ascender, which is what
   the mockup's heading measures - the family is fitted, not guessed. */
.feature__heading {
  margin: 0 -0.65385em 0.68077em -0.53846em;   /* full panel width, so it centres
                                                  on the panel, not the content box */
  font-size: 0.88462em;               /* 23px */
  font-weight: 600;
  line-height: 1;
  text-align: center;
  color: var(--heading-fg, #fff);
}

.feature__grid {
  flex: 1;
  min-height: 0;
  display: flex;
  gap: 0.42308em;                     /* 11px */
}

/* An announcement with nothing in the media box is just the copy, full width -
   no empty column held open for something that was never provided. */
.feature--text-only .feature__text { width: 100%; }

/* ...and a post with media but no words is the media alone, with the PANEL
   SHRUNK AROUND IT - the owner's request, 2026-08-26. It used to hold the full
   830px panel open and centre a 302px cover in it, which left a release with
   no caption sitting in a mostly-empty slab.

   Two things make it work:

     - the panel is `width: auto`, so it takes its width from its widest child
       and the border lands where the content ends. `max-width: var(--pw)` is
       the ceiling: a long heading, or a very wide image, stops at the size the
       panel has always been rather than running off across the artwork.
     - the media is sized from a HEIGHT. `--fh` is the tallest it may be, and
       the width it wants is that height times its own ratio - which is why the
       template writes `--ar` alongside `aspect-ratio`: a ratio cannot be read
       back out of `aspect-ratio` inside a calc(). The `min()` then clamps a
       very wide piece of media to the panel's own limit, and aspect-ratio
       turns the resulting width back into a height, so nothing is ever
       letterboxed or cropped.

   `--ar: 100` as the fallback is deliberate: media with no ratio at all (the
   SoundCloud player, which is a fixed 166px tall) makes the first term of the
   min() enormous, so the max-width term wins and it simply runs full width. */
.feature--media-only {
  --fh: calc(var(--pw) * 0.5);
  width: auto;
  max-width: var(--pw);
  height: auto;
  min-height: 0;
  /* The 20px of air either side the owner asked for. The border sits outside
     the padding box, so this really is 20px of panel between the border and
     the media rather than 20px that the border eats into. */
  padding-inline: 20px;
}
.feature--media-only .feature__grid { justify-content: center; }
.feature--media-only .feature__side { width: auto; }
.feature--media-only .feature__media {
  width: min(calc(var(--pw) - 40px), calc(var(--fh) * var(--ar, 100)));
  margin-inline: auto;
}
/* Media with a fixed pixel height rather than a ratio keeps that height; only
   its width comes from the rule above. */
.feature--media-only .feature__media[style*="height"] { width: calc(var(--pw) - 40px); }
/* .feature--media-flex sets a min-height further down and would otherwise
   hold this panel open to the mockup's full 524px with nothing in the space.
   Media-only is the one shape that has to fit its content in both directions. */
.feature--media-only.feature--media-flex { min-height: 0; }

/* The panel is a fixed 830x524 where the mockup defines it. Announcement media
   has no such obligation, and a tall photo should make the panel taller rather
   than be letterboxed into a shape it was never drawn for. */
.feature--media-flex {
  height: auto;
  min-height: calc(var(--pw) * 0.63133);
}

/* ---- left column: cover, then the streaming buttons under it ---- */

.feature__side {
  width: 11.61538em;                  /* 302px */
  display: flex;
  flex-direction: column;
}

/* The media box. A release cover is square, but an announcement can carry a
   16:9 video, a portrait photo or a fixed-height audio player, so the ratio
   comes from the content: the template sets aspect-ratio (or a pixel height
   for players that have no ratio) inline, and this only supplies the frame. */
.feature__media {
  display: grid;
  place-items: center;
  width: 100%;
  background: var(--box-bg, rgba(162, 106, 166, .94));
  /* Rounded to match the copy box beside it (2026-08-25). The mockup draws
     both square; the owner asked for the copy box first and the cover after,
     so the two now agree again. overflow is what makes the corners actually
     clip the cover art, the uploaded image or the embedded player - without
     it the radius only shows on the empty box's own background. */
  border-radius: var(--box-radius);
  overflow: hidden;
}
.feature__media--cover { aspect-ratio: 1 / 1; }

/* An upload with see-through pixels (2026-08-26, the owner's request): no box
   of its own, so a logo or a cut-out reads as part of the panel instead of
   sitting in a slab that has nothing to do with it.

   `transparent` and not `var(--panel-bg)`: the panel's own fill is
   translucent over the illustration, so painting a second copy of it here
   would stack the two and leave a visibly darker rectangle exactly where the
   box was supposed to stop existing. Letting the panel through IS the panel
   colour, at any opacity, including a fully opaque one.

   The image itself must not be cropped to a box shape either - there is no
   box to fill any more. */
.feature__media--alpha { background: transparent; }
.feature__media--alpha .feature__mediaEl { object-fit: contain; }

.feature__mediaEl {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
  object-fit: cover;
}
/* A remote image of unknown size has no ratio to reserve, so it sizes itself
   rather than being stretched into whatever box happens to be there. */
.feature__media:not([style*="aspect-ratio"]):not([style*="height"]) .feature__mediaEl {
  height: auto;
}

.feature__media--empty {
  aspect-ratio: 1 / 1;
  font-size: 0.91154em;               /* 23.7px */
  font-weight: 600;
  line-height: 1;
  /* grid centring puts the em box in the middle; the glyphs sit 2px below
     that, so the label is nudged back up onto the mockup's centre line */
  padding-bottom: 0.15385em;
  color: rgba(255, 255, 255, .82);
  /* the "no cover" prompt is a prompt - only the owner needs to see it */
  text-indent: -9999em;
}
body[data-admin-mode="on"] .feature__media--empty { text-indent: 0; }

.feature__platforms {
  margin-top: 0.80769em;              /* 21px */
  display: grid;
  grid-template-columns: repeat(4, 1.92308em);
  justify-content: center;
  column-gap: 0.58974em;              /* 15.3px */
  row-gap: 0.34615em;                 /* 9px */
}

/* the mockup's circles are flat, not the popup's gradient */
.feature .platform {
  width: 1.92308em;                   /* 50px */
  height: 1.92308em;
  background: var(--icon-bg, #ba48f6);
  color: var(--icon-fg, #fff);
  border: 0;
  box-shadow: none;
}
.feature .platform svg { width: 60%; height: 60%; }
.feature .platform:hover {
  transform: translateY(-2px) scale(1.05);
  box-shadow: 0 4px 10px rgba(43, 15, 46, .35);
}

/* ---- right column: the announcement copy ---- */

.feature__text {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  padding: 0.44231em 0.46154em;       /* 11.5 / 12px */
  background: var(--box-bg, rgba(162, 106, 166, .94));
  /* Owner's request, 2026-08-25: the big copy box gets rounded corners, and
     the cover box after it. The mockup draws both square, like the panel they
     sit in - all three are deliberate departures now. */
  border-radius: var(--box-radius);
}

.feature__copy {
  flex: 1;
  min-height: 0;
  margin: 0;
  overflow: hidden;
  font-family: var(--font-display);
  font-size: 0.62692em;               /* 16.3px - line 1 wraps at 408px, as drawn */
  font-weight: 600;
  line-height: 1.53374;               /* 25px */
  white-space: pre-wrap;
  color: rgba(255, 255, 255, .95);

  /* The copy sits in the MIDDLE of the box, not at the top of it - asked for
     on 2026-08-26, so a paragraph reads as centred without the owner padding
     it out with blank lines. Each paragraph is a flex item, so its own
     text-align still decides where it sits horizontally: this is vertical
     placement only and the alignment buttons are untouched.

     `safe center` is the important half. Plain `center` overflows a too-tall
     paragraph out of BOTH ends, so the first line becomes unreachable - and
     unreachable is not something "show more..." can fix, because the top is
     already gone. `safe` falls back to flex-start the moment the content does
     not fit, which is exactly the collapsed state the popup then expands. */
  display: flex;
  flex-direction: column;
  justify-content: center;
  justify-content: safe center;
}

/* Links typed into the announcement are always plain clickable text - never an
   embed, whatever they point at. They have to survive on a busy purple box, so
   they are underlined rather than relying on colour alone.

   `.rtbody` is worn by BOTH places the same copy is rendered - the panel's box
   and the "show more..." popup - so the marks are described once. */
.rtbody a {
  color: #fff;
  text-decoration: underline;
  text-underline-offset: 0.15em;
  text-decoration-thickness: 0.06em;
  overflow-wrap: anywhere;
  transition: opacity 160ms ease;
}
.rtbody a:hover { opacity: .78; }
.rtbody a:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--petal-200); border-radius: 2px; }

/* ---- formatted copy ----
   One .rt per paragraph, produced by src/richtext.js from the validated
   model. The paragraphs carry their own text-align inline; everything else
   here is just making sure the emphasis tags read on a busy purple box.

   No margins between paragraphs: the box is `white-space: pre-wrap`, so a
   blank line the owner typed is already a blank line. Adding one here would
   double it. */
.rtbody .rt { margin: 0; }

/* Bold has to gain weight against Poppins 600, which is already fairly heavy -
   700 is a visible step, 600 would not be. */
.rtbody strong { font-weight: 700; }
.rtbody em { font-style: italic; }
.rtbody u {
  text-decoration: underline;
  text-underline-offset: 0.15em;
  text-decoration-thickness: 0.06em;
}
.rtbody s { text-decoration: line-through; }

/* A highlight is a background on an inline run, so it sits tight against the
   glyphs. A little side padding and a soft corner stop it reading as a
   selection rectangle - and box-decoration-break keeps both ends rounded when
   the run wraps across a line. */
.rtbody span[style*="background-color"] {
  padding: 0 0.12em;
  border-radius: 0.15em;
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
}

/* Empty panel: the owner gets the prompt from the mockup, a visitor gets
   nothing - the whole panel is hidden for them further down. */
body[data-admin-mode="on"] .feature__copy:empty::after {
  content: 'type an announcement, insert an embed link to a video, new products in the shop, or link to a new music release. (no character limit.)';
  color: rgba(255, 255, 255, .72);
}

/* A pasted media link that could not be embedded, shown plainly under the copy. */
.feature__link {
  flex: none;
  margin: 0.30769em 0 0;
  font-size: 0.53846em;
  line-height: 1.5;
  overflow-wrap: anywhere;
}
.feature__link a { color: #fff; text-decoration: underline; text-underline-offset: 0.15em; }
.feature__link a:hover { opacity: .78; }

.feature__more {
  flex: none;
  align-self: center;
  margin-top: 0.30769em;
  padding: 0.15em 0.4em;
  border: 0;
  border-radius: var(--radius-pill);
  background: transparent;
  color: rgba(255, 255, 255, .95);
  font-family: var(--font-display);
  font-size: 0.53846em;               /* 14px */
  cursor: pointer;
  transition: background-color 180ms ease, transform 180ms ease;
}
.feature__more:hover  { background: rgba(255, 255, 255, .14); }
.feature__more:active { transform: scale(.97); }
.feature__more:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--petal-200); }
.feature__more[hidden] { display: none; }

/* ---- the deck: one panel per post, one shown at a time ----

   The deck itself draws nothing and is not positioned: every .feature is
   still `position: fixed` and places itself, exactly as it did when there was
   only ever one, so the measured geometry is untouched. The wrapper exists so
   home.js has one place to listen for a click, a swipe or an arrow key.

   The arrows hang off the panel's own edges rather than off the viewport,
   because a media-only panel is a different width from a release one and the
   arrows have to follow it. .feature is fixed, so it is the containing block
   for them and a negative offset puts them outside it - the same trick the
   "..." edit handle has always used. */
.feature__nav {
  position: absolute;
  top: 50%;
  width: 1.92308em;                   /* 50px - the asset's own width */
  height: 4.46154em;                  /* 116px */
  transform: translateY(-50%);
  padding: 0;
  border: 0;
  background: transparent center / contain no-repeat;
  cursor: pointer;
  z-index: 2;
  transition: transform 180ms cubic-bezier(.2, .9, .3, 1.25), opacity 180ms ease;
}
.feature__nav--prev {
  left: -2.65385em;                   /* 69px clear of the panel edge */
  background-image: url('/static/assets/left-arrow.png');
}
.feature__nav--next {
  right: -2.65385em;
  background-image: url('/static/assets/right-arrow.png');
}
.feature__nav:hover { transform: translateY(-50%) scale(1.07); }
.feature__nav:active { transform: translateY(-50%) scale(.96); }
.feature__nav:focus-visible {
  outline: none;
  border-radius: var(--radius-sm);
  box-shadow: 0 0 0 3px var(--petal-200);
}
/* At either end of the list there is nowhere to go, so the control is not
   drawn at all rather than drawn dead. */
.feature__nav[hidden] { display: none; }

/* Nothing entered yet and nobody signed in: show no empty panel at all. */
.feature.is-blank { display: none; }
body[data-admin-mode="on"] .feature.is-blank { display: flex; }

/* THE SLIDE THAT IS NOT SHOWING.

   `hidden` is only `display: none` in the UA stylesheet, so ANY author rule
   that sets display beats it - and .feature sets `display: flex`. Without
   this every post painted on top of every other one at the same fixed
   position, and the page showed whichever came last in the markup. Same trap
   as the <dialog> in hard-won rule 4, and it looks like the code working
   until you notice the wrong post is on screen.

   The second selector is not redundant. `body[data-admin-mode="on"]
   .feature.is-blank` is (0,3,1) and would otherwise out-specify a plain
   `.feature[hidden]` at (0,2,0), so a signed-in owner with an empty post in
   the deck would see it stacked on top of the others. */
.feature[hidden],
body[data-admin-mode="on"] .feature.is-blank[hidden] { display: none; }

/* ---- admin handle, same "..." affordance as a carousel cover ---- */

.feature__edit {
  position: absolute;
  top: -0.42308em;
  right: -0.42308em;
  width: 1.15385em; height: 1.15385em;
  display: grid; place-items: center;
  border-radius: var(--radius-pill);
  background: rgba(150, 62, 158, .92);
  border: 1px solid rgba(255, 255, 255, .45);
  color: #fff;
  font-size: 0.65385em; line-height: 1;
  cursor: pointer;
  z-index: 3;
  box-shadow: var(--shadow-cover);
  transition: transform 180ms cubic-bezier(.2, .9, .3, 1.25), background-color 180ms ease;
}
.feature__edit:hover { transform: scale(1.1); background: rgba(180, 92, 190, .96); }
.feature__edit:active { transform: scale(.94); }
.feature__edit:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--petal-200); }

/* ---------------------------------------------------------------
   Mobile - references/home-page-mockup-mobile.png, measured at a
   375x812 stage (the mockup is 1125x2436, 3x):

     panel      x 26..349, y 115..647     -> 324 x 533
     heading    glyphs x 68..307, top 135
     cover      x 68..307, y 166..405     -> 240 square
     copy box   x 51..329, y 413..482     -> 279 x 70, three lines
     show more  ~y 492..502, hairline under it at y 504
     buttons    x 56..306, rows y 523..572 and 582..631

   The block stacks: cover, copy, show more, then the buttons - so
   .feature__side and .feature__text collapse to `display: contents`
   and the children are ordered directly. Type keeps the desktop em
   ratios; only the panel's base size changes.
   --------------------------------------------------------------- */

@media (max-width: 760px) {
  .feature {
    /* Every length in this block is a multiple of --u, so the whole panel
       scales as one piece. That matters: the panel's height comes from its
       width, and on a short-but-wide phone it grew taller than the space
       between the hamburger and the footer, so centring shoved it up under
       the hamburger. Measured on real viewports before this: 412x764 left a
       15px gap, 412x730 zero, 360x640 actually overlapped.

       --u is 1vw until height becomes the binding limit, then it shrinks.
       0.49vh is the largest value that still clears the hamburger on the
       tightest phone measured (360x640), and it does not bind at 375x812, so
       the mockup match is untouched. Sizing the children in raw vw is what
       broke this - capping the panel width alone did nothing to its height. */
    /* THE x0.9537 IS THE WHOLE PANEL COMING DOWN 15px, asked for on
       2026-08-26 so it does not crowd the hamburger on a small phone (the
       owner said "10px to 20px"; 15px is the middle). 324 -> 309px.

       It multiplies --u and NOT --pw, and that distinction cost a round trip.
       Everything in this block is a multiple of --u - the type, the button
       circles, the gaps - and only the panel's own width is written against
       --pw. Shrinking --pw alone therefore narrowed the panel and left its
       height, its heading and its buttons at full size, which is the same
       mistake as the raw-vw sizing described above, one level up.

       The measuring scripts carry this the way they carry the 16px drop:
       MOBILE_SHRINK in measure-home.mjs scales the mockup's boxes about the
       panel's centre, so real drift still shows rather than being rebaselined
       into silence. */
    --u: calc(min(1vw, 0.49vh) * 0.9537);
    --pw: calc(var(--u) * 86.4);      /* 324 mockup px, so 309 after the shrink */
    /* The desktop fraction of --pw would give 3.9px here, which is far too
       tight on a 70px-tall copy box. Pinned to 9px instead - the weight the
       desktop 10px reads at, scaled to these boxes. */
    --box-radius: calc(var(--u) * 2.4);
    left: 50%;
    /* 2% lower than centre, at the owner's request, for air under the hamburger */
    top: 48.9%;
    width: var(--pw);
    height: auto;
    padding: calc(var(--u) * 4.32) 0 calc(var(--u) * 2.4);          /* 22.2 / 15px, less the 6px border */
    align-items: center;
    font-size: calc(var(--u) * 4.624);               /* 17.34px - the desktop base x 0.667 */
  }

  .feature__heading {
    margin: 0 0 calc(var(--u) * 3.68);               /* 13.8px */
    line-height: 1;
  }

  .feature__grid {
    flex: none;
    flex-direction: column;
    align-items: center;
    width: 100%;
    gap: 0;
  }

  /* the two desktop columns disappear; their children stack in mockup order */
  .feature__side,
  .feature__text { display: contents; }

  .feature__media {
    order: 1;
    width: calc(var(--u) * 64);                      /* 240px */
  }
  .feature--text-only .feature__copy,
  .feature--text-only .feature__more { width: calc(var(--u) * 86.4); }

  /* the mockup sets this label 11px above the box centre, not on it */
  .feature__media--empty { padding-bottom: calc(var(--u) * 5.87); }

  /* The copy does not scale with the heading here: the mockup sets it at
     9.16px against a 15.34px heading, so it gets its own size. The box is a
     fixed 69.3px - three 14px lines sitting high inside it, as drawn. */
  .feature__copy {
    order: 2;
    flex: none;
    width: calc(var(--u) * 74.32);                   /* 278.7px */
    height: calc(var(--u) * 18.48);                  /* 69.3px */
    margin-top: calc(var(--u) * 1.87);               /* 7px */
    /* Three lines, exactly as drawn, with the rest behind "show more...".

       This used to be `-webkit-line-clamp: 3` on a -webkit-box, and that only
       works while the copy's children are inline. Formatted copy is a list of
       block paragraphs, each carrying its own alignment, and against those the
       clamp stops laying out and DRAWS THEM ON TOP OF EACH OTHER - the third
       paragraph printed over the second, unreadable. Found on 2026-08-25 on a
       375x812 render.

       The window is made out of the box's own geometry instead, which does not
       care what is inside it. Two things make that work, and neither is
       optional:

         - `overflow: hidden` (from the desktop rule) clips at the PADDING box,
           not the content box, so bottom padding cannot hide a fourth line -
           text simply paints over it. That was the first attempt and it left
           line four half-showing. The space that hides it has to be a BORDER,
           which is outside the clip.
         - the border is painted in the box's own colour, and
           `background-clip: padding-box` stops the fill at the padding edge so
           the two translucent layers do not stack into a darker band.

       The mockup's box is 70px tall with three lines sitting high inside it:
         18.48u total = 1.47u padding + 11.2u (three 14px lines) + 5.81u border */
    padding: calc(var(--u) * 1.47) calc(var(--u) * 2) 0;
    border-bottom: calc(var(--u) * 5.81) solid var(--box-bg, rgba(162, 106, 166, .94));
    background-clip: padding-box;
    font-size: 0.528em;               /* 9.16px */
    line-height: 1.5293;              /* 14px */
    background-color: var(--box-bg, rgba(162, 106, 166, .94));
    /* Mobile puts the fill on the copy itself - .feature__text is
       `display: contents` here - so the rounding has to come with it. */
    border-radius: var(--box-radius);
    /* The owner's call, 2026-08-26: on a phone the preview reads from the TOP
       and the centring happens in the popup instead. Three lines centred in a
       three-line window is the same thing anyway; from the top it is obvious
       there is more to open. */
    justify-content: flex-start;
  }

  .feature__more {
    order: 3;
    width: calc(var(--u) * 75.5);                    /* 283px - the hairline's width */
    margin-top: calc(var(--u) * 1.6);                /* 6px */
    padding: 0 0 calc(var(--u) * 1.6);
    line-height: 1;
    border-radius: 0;
    border-bottom: 1px solid rgba(255, 255, 255, .45);
  }

  .feature__platforms {
    order: 4;
    margin-top: calc(var(--u) * 4.8);                /* 18px */
    column-gap: calc(var(--u) * 4.44);               /* 16.7px */
    row-gap: calc(var(--u) * 2.67);                  /* 10px */
    grid-template-columns: repeat(4, calc(var(--u) * 13.33));
  }

  .feature .platform { width: calc(var(--u) * 13.33); height: calc(var(--u) * 13.33); }   /* 50px */

  .feature__edit { top: calc(var(--u) * 2.13); right: calc(var(--u) * 2.13); }

  /* No arrows on a phone: there is no room for them either side of a panel
     that is already nearly the width of the screen, and the gesture people
     expect here is a swipe. home.js listens for that on the deck. */
  .feature__nav { display: none; }

  /* The mobile .feature rule above resets `padding` wholesale, and the mobile
     .feature__media rule pins a width, so media-only has to restate both here
     - the panel keeps its normal width on a phone (it is narrow already) and
     only the media grows into it, less the 20px either side. */
  .feature--media-only {
    --fh: calc(var(--u) * 44);
    width: var(--pw);
    padding: calc(var(--u) * 4.32) 20px calc(var(--u) * 2.4);
  }
  .feature--media-only .feature__media {
    width: min(calc(var(--pw) - 40px), calc(var(--fh) * var(--ar, 100)));
  }
  .feature--media-only .feature__media[style*="height"] { width: calc(var(--pw) - 40px); }
}

/* ---------------------------------------------------------------
   "show more..." - the announcement popup, added 2026-08-26.

   It used to expand the box in place, which on a phone meant a scrollbar
   inside a box three lines tall. It now opens the same <dialog> shell the
   release popup uses (popup.css), so the two "show more" controls on the site
   behave the same way.

   The dialog carries the post's own colours, so what opens reads as the box
   the reader just tapped rather than as a different component.
   --------------------------------------------------------------- */

.popup__panel--say {
  /* Wider than the release popup: this is a wall of text, not a cover and a
     row of buttons. */
  width: min(92vw, 720px);
  max-height: min(86vh, 760px);
  display: flex;
  flex-direction: column;
  gap: 0.7em;
  padding: 16px 16px 20px;
  background: var(--panel-bg, rgba(229, 194, 243, .78));
  border: 6px solid var(--panel-border, rgba(194, 148, 203, .94));
  border-radius: var(--radius-md);
  font-family: var(--font-display);
}

.popup__title--say {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(17px, 2.4vw, 23px);
  font-weight: 600;
  line-height: 1.15;
  text-align: center;
  color: var(--heading-fg, #fff);
}

/* The copy, in the shape the desktop panel gives it: one box, the text
   centred in it vertically, each paragraph keeping its own alignment. The
   min-height is what makes the centring mean anything for a short
   announcement - without it the box would shrink to the text and there would
   be nothing to be centred in. */
.say {
  flex: 1;
  min-height: min(42vh, 430px);
  max-height: 100%;
  overflow-y: auto;
  overscroll-behavior: contain;
  /* the scrollbar itself is styled site-wide in site.css, and takes this
     post's --scroll-thumb / --scroll-track when it has set them */

  display: flex;
  flex-direction: column;
  justify-content: center;
  justify-content: safe center;   /* see .feature__copy for why `safe` */

  padding: 14px 16px;
  background: var(--box-bg, rgba(162, 106, 166, .94));
  border-radius: var(--box-radius, 10px);

  font-size: clamp(14px, 1.05vw, 17px);
  font-weight: 600;
  line-height: 1.55;
  white-space: pre-wrap;
  color: rgba(255, 255, 255, .95);
}

.popup__panel--say .feature__link {
  flex: none;
  margin: 0;
  font-size: 13px;
  text-align: center;
}

@media (max-width: 760px) {
  .popup__panel--say {
    width: 100%;
    padding: 13px 12px 16px;
    border-width: 4px;
  }
  .popup__title--say { font-size: 17px; }
  .say {
    min-height: 46vh;
    padding: 12px 13px;
    font-size: 14px;
    line-height: 1.6;
  }
}
