/* ==========================================================================
   LiveSquirtCams

   One stylesheet, no framework, no build step. Bump 'asset_v' in config.php
   after editing this file or caches will hold the old one for a week.

   THE PALETTE IS FIXED. Every colour below is one of the nine in the brand
   sheet, and nothing else may be introduced. Two of them are near enough to
   read as one at a glance and they do different jobs, so keep them apart:
   --red is chrome that is currently active, --red-deep is chrome at rest.

     --topbar     #730001   the top bar, so the logo is legible on it
     --red        #E10613   active rail item, thumbnail frame, hovers
     --red-deep   #9B0B14   the rail, the footer, hover states, panels
     --ground     #3A0508   the well the grid sits in, and the page behind it
     --gold       #FFD100   every call to action, and only calls to action
     --gold-hi    #E6B800   the same, hovered
     --white      #FFFFFF   body text on red, model names
     --offwhite   #F5F5F5   light chips and light panels
     --ink        #1A1A1A   text on gold, because gold on white cannot be read
     --live       #22C55E   the free-chat dot, and nothing else

   Gold is the scarcest thing here on purpose. The moment it appears on a
   border or a heading it stops meaning "click this", and every button on the
   site gets quieter at once.
   ========================================================================== */

:root {
  /* The top bar, and it is NOT --red. The wordmark's letterforms are a bright
     red with a gold outline, and on a bright red bar only the outline carried
     it: the letters themselves sank into the background. This is close to the
     field the logo artwork was drawn on, so the mark reads as intended.
     --red stays what it was and still marks the active rail item, the
     thumbnail frame and every hover. */
  --topbar:    #730001;

  --red:       #E10613;
  --red-deep:  #9B0B14;
  --ground:    #3A0508;

  --gold:      #FFD100;
  --gold-hi:   #E6B800;

  --white:     #FFFFFF;
  --offwhite:  #F5F5F5;
  --ink:       #1A1A1A;

  --live:      #22C55E;
  --dim:       rgba(0, 0, 0, .45);
  --line:      rgba(255, 255, 255, .12);

  /* Derived, not new colours: the same three reds at reduced opacity so a
     panel can sit on the ground without a tenth hue appearing. */
  --panel:     rgba(155, 11, 20, .38);
  --panel-2:   rgba(155, 11, 20, .60);
  --muted:     rgba(255, 255, 255, .72);
  --muted-2:   rgba(255, 255, 255, .52);

  /* --hdr is the single source of truth for the bar height, and it also feeds
     the player height on the model page, so the two can never fall out of
     step. --rail does the same job for the category column. */
  --hdr:       82px;
  --rail:      212px;
  /* The model page action bar. Not a fixed height (her name wraps at some
     widths), so this is what the sticky sidebar offsets itself by and the
     error is absorbed by that sidebar having a max-height of its own. */
  --abar:      92px;

  --radius:    10px;
  --radius-sm: 6px;
  --shadow:    0 18px 44px rgba(0, 0, 0, .5);

  --sans: 'Barlow', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --disp: 'Alegreya', Georgia, 'Times New Roman', serif;
}

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--ground);
  color: var(--white);
  font: 400 16px/1.6 var(--sans);
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; height: auto; display: block; }
a { color: var(--white); text-decoration: none; }
a:hover { text-decoration: underline; }

::selection { background: var(--gold); color: var(--ink); }
:focus-visible { outline: 2px solid var(--gold); outline-offset: 3px; border-radius: 4px; }

/* --------------------------------------------------------------------------
   Scrollbars, on the three boxes that scroll inside the page

   The category rail, the facts column on a model page and the language picker
   each have their own scroll. Left alone they render the operating system's
   default bar, measured here at 15px of light grey with a raised thumb: the
   brightest object on a dark red page, and on the rail it was nearly as wide
   as the count badges sitting next to it.

   Measured after: 11px, no track, translucent white thumb. The width drop is
   only 4px and it is not where most of the improvement comes from. Killing the
   track is: a grey channel running the full height of the rail was doing more
   to make it look heavy than the thumb ever did.

   Read the @supports note below before changing any of this. The two syntaxes
   cannot both apply, which is not obvious and cost a measurement to find.

   The page's own scrollbar is deliberately untouched. Restyling the window
   scrollbar is the kind of thing that reads as broken rather than designed,
   and it is the one bar a visitor expects to look like their computer.
   -------------------------------------------------------------------------- */

.rail,
.dos-side,
.lang-list {
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, .22) transparent;
}

/* THE TWO SYNTAXES CANNOT BOTH APPLY, so this is guarded rather than stacked.
   Setting scrollbar-width to anything other than auto makes Chromium ignore
   ::-webkit-scrollbar completely. Written without the @supports, the block
   below is dead code in every current browser: the rail measured 11px, which
   is what scrollbar-width:thin gives, not the 6px asked for here.
   So the standard properties above lead, and this only runs where they are not
   understood, which today is older Safari and older Chromium. Those get 6px;
   everywhere else gets the browser's own "thin", which is 11px and correct
   enough. Do not delete the @supports to "make it work" in Chrome. It already
   works; removing the guard would just turn one rule off and the other on. */
@supports not (scrollbar-width: thin) {
  .rail::-webkit-scrollbar,
  .dos-side::-webkit-scrollbar,
  .lang-list::-webkit-scrollbar { width: 6px; height: 6px; }

  .rail::-webkit-scrollbar-track,
  .dos-side::-webkit-scrollbar-track,
  .lang-list::-webkit-scrollbar-track { background: transparent; }

  .rail::-webkit-scrollbar-thumb,
  .dos-side::-webkit-scrollbar-thumb,
  .lang-list::-webkit-scrollbar-thumb {
    background: rgba(255, 255, 255, .2);
    border-radius: 999px;
  }
  .rail:hover::-webkit-scrollbar-thumb,
  .dos-side:hover::-webkit-scrollbar-thumb,
  .lang-list::-webkit-scrollbar-thumb:hover {
    background: rgba(255, 255, 255, .38);
  }
}

/* Screen reader only. Used by the free-chat badge, which is a dot on screen
   and a full sentence to a screen reader. */
.vh {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--gold); color: var(--ink);
  padding: 10px 16px; border-radius: 0 0 var(--radius-sm) 0; font-weight: 700;
}
.skip:focus { left: 0; }

h1, h2, h3 { font-family: var(--disp); font-weight: 700; line-height: 1.15; margin: 0; }

.h1 { font-size: clamp(26px, 3.4vw, 40px); letter-spacing: -.01em; }
.h1-page {
  font-family: var(--sans); font-size: .42em; font-weight: 600;
  vertical-align: middle; color: var(--muted-2); margin-left: 10px;
}
.h2 { font-size: clamp(20px, 2.2vw, 27px); margin: 0 0 12px; }
.h3 { font-size: 17px; margin: 0 0 8px; }

/* --------------------------------------------------------------------------
   Top bar
   -------------------------------------------------------------------------- */

.top {
  position: sticky; top: 0; z-index: 40;
  height: var(--hdr);
  background: var(--topbar);
  border-bottom: 1px solid rgba(0, 0, 0, .25);
}
.top-in {
  height: 100%; display: flex; align-items: center; gap: 24px;
  padding: 0 20px;
}

/* The bar is sized around the mark rather than the other way round. The script
   tagline in the logo stops being readable much below 52px, so the bar is tall
   enough to carry it and the phone breakpoint drops both together. */
.brand { display: flex; align-items: center; flex: 0 0 auto; }
.brand:hover { text-decoration: none; }
.brand img {
  width: auto; height: 54px;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, .45));
}

.top-nav {
  display: flex; align-items: center; gap: 22px;
  margin-left: auto;
  font-size: 14px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em;
}
.nav-links { display: flex; align-items: center; gap: 9px; }

/* Pills rather than bare words. Three plain links in a row read as body text
   that happens to be in the header; an outline turns each one into a target
   and gives the bar something to be built around.
   No gold anywhere in here on purpose. Gold means "this is the click that
   earns" and it is spent on the model page button; spreading it across the
   navigation would make every one of those buttons quieter. */
.nav-btn {
  padding: 8px 15px;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, .22);
  color: rgba(255, 255, 255, .9);
  font-size: 12.5px; font-weight: 600;
  text-transform: uppercase; letter-spacing: .05em;
  white-space: nowrap;
  transition: background .16s ease, border-color .16s ease, color .16s ease;
}
.nav-btn:hover {
  background: rgba(255, 255, 255, .12);
  border-color: rgba(255, 255, 255, .45);
  color: var(--white);
  text-decoration: none;
}
/* The section you are in. Solid --red against the darker --topbar, so it reads
   as filled without introducing a colour the bar does not already own. */
.nav-btn.on {
  background: var(--red);
  border-color: var(--red);
  color: var(--white);
}

/* --------------------------------------------------------------------------
   The burger

   A <details>, so it works with JavaScript blocked and the links inside it are
   real links. Hidden on desktop, where .nav-links carries the same array.
   -------------------------------------------------------------------------- */

.menu { display: none; position: relative; }

.burger {
  list-style: none; cursor: pointer;
  /* 44 square, which is the minimum comfortable touch target and not a round
     number picked by eye. It was 42x38 and measured as failing that: this is
     the only control in the header on a phone, so it is the last one that
     should be fiddly to hit. It still clears the 62px mobile bar. */
  width: 44px; height: 44px;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid rgba(255, 255, 255, .22);
  border-radius: var(--radius-sm);
}
.burger::-webkit-details-marker { display: none; }
.burger::marker { content: ''; }

/* Three bars from one element: the span is the middle, its two pseudo elements
   are the outer two. They fold into an X when the menu is open. */
.burger > span,
.burger > span::before,
.burger > span::after {
  display: block; width: 18px; height: 2px;
  background: var(--white); border-radius: 2px;
  transition: transform .2s ease, background .2s ease;
}
.burger > span { position: relative; }
.burger > span::before { content: ''; position: absolute; top: -6px; }
.burger > span::after  { content: ''; position: absolute; top: 6px; }

.menu[open] .burger { border-color: rgba(255, 255, 255, .45); }
.menu[open] .burger > span { background: transparent; }
.menu[open] .burger > span::before { transform: translateY(6px) rotate(45deg); }
.menu[open] .burger > span::after  { transform: translateY(-6px) rotate(-45deg); }

@media (prefers-reduced-motion: reduce) {
  .nav-btn, .burger > span, .burger > span::before, .burger > span::after { transition: none; }
}

.menu-panel {
  position: absolute; right: 0; top: calc(100% + 10px); z-index: 60;
  min-width: 210px; padding: 8px;
  display: flex; flex-direction: column; gap: 4px;
  background: var(--red-deep);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}
.menu-link {
  /* 13px of vertical padding rather than 11, for the same reason as the burger
     above: it puts each row at roughly 44px, so the whole menu is thumb sized
     rather than only the button that opened it. */
  padding: 13px 14px; border-radius: var(--radius-sm);
  font-size: 14.5px; font-weight: 600; color: var(--white);
  white-space: nowrap;
}
.menu-link:hover { background: var(--red); text-decoration: none; }
.menu-link.on { background: var(--red); }

/* Language picker. A disclosure of real links, so it works with JavaScript
   blocked. With one language configured it never renders at all. */
.lang { position: relative; }
.lang > summary {
  list-style: none; cursor: pointer; display: flex; align-items: center; gap: 7px;
  padding: 6px 10px; border: 1px solid var(--line); border-radius: var(--radius-sm);
}
.lang > summary::-webkit-details-marker { display: none; }
.lang-code { font-weight: 700; }
.lang-now { color: rgba(255, 255, 255, .8); font-weight: 500; text-transform: none; }
.lang-list {
  position: absolute; right: 0; top: calc(100% + 8px); z-index: 50;
  margin: 0; padding: 6px; list-style: none;
  min-width: 190px; max-height: 70vh; overflow: auto;
  background: var(--red-deep); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--shadow);
}
.lang-list a {
  display: block; padding: 8px 12px; border-radius: var(--radius-sm);
  font-weight: 500; text-transform: none; letter-spacing: 0;
}
.lang-list a:hover { background: var(--red); text-decoration: none; }
.lang-list a[aria-current] { color: var(--gold); }

/* --------------------------------------------------------------------------
   The shell: category rail on the left, content well on the right

   The rail is the site's main navigation and it carries a live count on every
   line, which is only affordable because every category is a filter over one
   cached pool. It costs no API call.
   -------------------------------------------------------------------------- */

.shell { display: block; }

.rail { display: none; }

.wrap { max-width: 1560px; margin: 0 auto; padding: 22px 20px 60px; }

@media (min-width: 1100px) {
  body.has-rail .shell {
    display: grid;
    grid-template-columns: var(--rail) minmax(0, 1fr);
    align-items: start;
  }
  body.has-rail .rail {
    display: block;
    position: sticky; top: var(--hdr);
    max-height: calc(100vh - var(--hdr));
    overflow-y: auto; overscroll-behavior: contain;
    background: var(--red-deep);
    padding: 14px 0 28px;
    border-right: 1px solid rgba(0, 0, 0, .25);
  }
  body.has-rail .wrap { max-width: 1400px; margin: 0; padding-left: 26px; }
}

.rail-item {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 9px 18px;
  font-size: 14.5px; font-weight: 600; line-height: 1.25;
  color: rgba(255, 255, 255, .9);
  border-left: 3px solid transparent;
}
.rail-item:hover { background: var(--red); color: var(--white); text-decoration: none; }
.rail-item.on { background: var(--red); border-left-color: var(--gold); color: var(--white); }
.rail-item.soft { font-weight: 500; color: rgba(255, 255, 255, .74); font-size: 14px; }
.rail-item.all { color: var(--gold); margin-top: 10px; font-size: 13.5px; }
.rail-n {
  flex: 0 0 auto; font-size: 11.5px; font-weight: 700; font-variant-numeric: tabular-nums;
  padding: 2px 7px; border-radius: 999px;
  background: rgba(0, 0, 0, .28); color: rgba(255, 255, 255, .8);
}
.rail-item.on .rail-n { background: rgba(0, 0, 0, .35); color: var(--white); }
.rail-head {
  margin: 20px 0 6px; padding: 0 18px;
  font-size: 11px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase;
  color: rgba(255, 255, 255, .55);
}

/* --------------------------------------------------------------------------
   Page furniture
   -------------------------------------------------------------------------- */

.crumb {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  font-size: 13px; color: var(--muted-2); margin-bottom: 14px;
}
.crumb a { color: var(--muted); }

.page-head { margin-bottom: 18px; }
.lede { margin: 10px 0 0; color: var(--muted); font-size: 17px; max-width: 68ch; }

.prose { color: var(--muted); max-width: 74ch; }
.prose p { margin: 0 0 14px; }
.prose a { color: var(--gold); }
.prose h2 { color: var(--white); margin: 30px 0 10px; max-width: none; }
.prose h2:first-child { margin-top: 0; }

.intro {
  background: var(--panel); border: 1px solid var(--line);
  border-left: 3px solid var(--red);
  border-radius: var(--radius); padding: 16px 20px; margin-bottom: 22px;
  max-width: none;
}
.intro p:last-child { margin-bottom: 0; }

.seo { max-width: none; margin-top: 44px; padding-top: 30px; border-top: 1px solid var(--line); }
.seo p { max-width: 74ch; }

.section-title { margin: 34px 0 14px; }

/* The chip strip under the H1 on the home page. Page 1 only, and it links
   every written category from the strongest URL on the site. */
.tag-strip { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 20px; }
.tag-strip a {
  padding: 7px 14px; border-radius: 999px;
  background: var(--panel-2); border: 1px solid var(--line);
  font-size: 13.5px; font-weight: 600;
}
.tag-strip a:hover { background: var(--red); text-decoration: none; }
.tag-strip .more { background: transparent; color: var(--gold); }

.grid-bar {
  display: flex; align-items: center; gap: 9px;
  margin: 0 0 16px; font-size: 14.5px; color: var(--muted);
}
.grid-bar strong { color: var(--white); font-size: 17px; font-variant-numeric: tabular-nums; }

.pulse {
  width: 9px; height: 9px; border-radius: 50%; background: var(--live);
  box-shadow: 0 0 0 0 rgba(34, 197, 94, .7); animation: pulse 2s infinite;
}
@keyframes pulse {
  70%  { box-shadow: 0 0 0 9px rgba(34, 197, 94, 0); }
  100% { box-shadow: 0 0 0 0 rgba(34, 197, 94, 0); }
}
@media (prefers-reduced-motion: reduce) {
  .pulse { animation: none; }
  html { scroll-behavior: auto; }
}

/* --------------------------------------------------------------------------
   The grid

   The card is the photograph. Name and age sit on a scrim over the bottom of
   the picture rather than in a strip underneath it, so no vertical space goes
   to anything but her. The tile is 4:3 and the image is the feed's 800x600,
   which is also 4:3, so nothing is cropped at any column width.
   -------------------------------------------------------------------------- */

.grid {
  display: grid; gap: 12px;
  /* 140 and not 158, and the reason is load bearing rather than taste.
     On a 320px screen the grid is 296px wide. At a 158px minimum, auto-fill
     fits exactly ONE explicit column. The promo tile's `span 2` then forces an
     IMPLICIT second column, and implicit tracks are sized `auto`, which is what
     produced a measured grid of "157.992px 126.249px": two columns of different
     widths.
     The knock-on is worse than the ragged edge. `-1` resolves to the end of the
     EXPLICIT grid only, so `grid-column: 1 / -1` on the video row spanned that
     one explicit track and rendered 158px wide inside a 296px grid, with holes
     around it.
     At 140px two explicit columns fit at 320px ((296 + 12) / (140 + 12) = 2.02),
     so there are no implicit columns for either item to fall foul of. Do not
     raise this number without re-checking 320px. */
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  /* The promo tile spans two columns, and the number of columns changes with
     the viewport, so there is no position it can sit at that always lands on a
     boundary. Without dense flow it drops to the next row whenever fewer than
     two columns are left, leaving a visible hole where a card should be.
     Dense backfills that hole with the next card that fits, which is the very
     next one, because every other tile is a single column. The cost is that
     one card moves up by one position; the alternative is an empty slot in the
     middle of the grid. */
  grid-auto-flow: row dense;
}
@media (min-width: 720px)  { .grid { grid-template-columns: repeat(auto-fill, minmax(224px, 1fr)); gap: 14px; } }
@media (min-width: 1440px) { .grid { grid-template-columns: repeat(auto-fill, minmax(268px, 1fr)); } }

.card {
  position: relative; display: block; overflow: hidden;
  aspect-ratio: 4 / 3;
  /* Keep the 4:3 even when something else makes the row taller.
     The promo tile carries a min-height, so the row it sits in can be a little
     taller than a card. Grid items stretch to their row by default, and that
     stretch overrides aspect-ratio, which would leave one row of cards subtly
     the wrong shape. start pins them to their own height instead. */
  align-self: start;
  border-radius: var(--radius);
  /* The thin red frame the brand sheet asks for around every thumbnail. */
  border: 1px solid var(--red);
  background: var(--red-deep);
}
.card:hover { text-decoration: none; }
.card-img { width: 100%; height: 100%; object-fit: cover; transition: transform .35s ease; }
.card:hover .card-img { transform: scale(1.045); }
@media (prefers-reduced-motion: reduce) { .card-img, .card:hover .card-img { transition: none; transform: none; } }

.card-veil {
  position: absolute; inset: auto 0 0 0; height: 58%;
  background: linear-gradient(180deg, transparent 0%, var(--dim) 60%, rgba(0, 0, 0, .78) 100%);
  pointer-events: none;
}

.card-bar {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: 9px 11px;
}
/* The name row. It was the bar itself until her room topic arrived underneath,
   and the bar became a column of two. */
.card-id { display: flex; align-items: baseline; gap: 7px; }
.card-name {
  font-weight: 700; font-size: 14.5px; color: var(--white);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.card-age { font-size: 12.5px; color: rgba(255, 255, 255, .7); font-variant-numeric: tabular-nums; }

/* --------------------------------------------------------------------------
   Her room topic on the card

   The line she pinned above her own room, carried on every tile. It is the
   only per-model prose a listing page has, so it earns its place, but it is
   emphatically secondary: her photograph is what somebody is looking at, and
   a permanent second line of text on 300 tiles would be noise.

   So it is collapsed to nothing and opens on hover. Collapsed, NOT
   display:none: the text stays in the markup and in the layout, which is the
   whole reason for putting it there. A tile is roughly 34 characters wide, so
   anything longer is marked 'long' in PHP and drifts sideways while the
   pointer rests on the card, which is the only way to read the end of it
   without giving the line two rows of permanent space.
   -------------------------------------------------------------------------- */

.card-topic {
  display: block;
  overflow: hidden;
  white-space: nowrap;
  max-height: 0;
  opacity: 0;
  font-size: 11.5px; line-height: 1.45; font-weight: 500;
  color: rgba(255, 255, 255, .78);
  transition: max-height .22s ease, opacity .22s ease, margin-top .22s ease;
}
.card:hover .card-topic,
.card:focus-visible .card-topic {
  max-height: 17px;
  opacity: 1;
  margin-top: 3px;
}

/* No will-change here on purpose. It is a promise to the compositor that costs
   memory per element, and there can be 300 of these on a page while at most
   one is ever animating. The browser promotes the layer on its own when the
   animation starts, which is soon enough. */
.ct-run { display: inline-block; }

/* ONE duration, deliberately.
   Two cleverer versions came before this and both were removed. Scaling the
   duration by character count in PHP does not work, because the count does not
   predict the overflow: a line that just misses fitting spills a few pixels
   while one half again as long spills hundreds. Measuring the real overflow in
   JavaScript does work, but it means a document level listener and a forced
   layout per card to smooth out an effect nobody is timing with a stopwatch.
   A flat 3s is faster than the 7s that prompted the complaint, costs nothing,
   and is one number to change if it still is not right.
   linear rather than ease-in-out so the whole three seconds is spent moving at
   a readable pace instead of accelerating and braking. */

/* Alternate rather than a loop, so the line slides out and comes back instead
   of jumping to the start. No duplicated copy of the text either: a marquee
   that loops seamlessly needs the string twice in the markup, and printing her
   topic twice per tile is exactly the sort of thing that reads as keyword
   padding. One copy, sliding. */
/* FALLBACK PATH, for browsers without container query units.
   Only the lines PHP guessed were long, moved by 55% of their own width. The
   guess is a character count and a character count cannot be right at every
   column width, so it is deliberately conservative: it would rather leave a
   long line still when it fits than slide a short one that does not need it. */
.card:hover .card-topic.long .ct-run {
  animation: ct-run 3s linear .3s infinite alternate;
}
@keyframes ct-run {
  from { transform: translateX(0); }
  to   { transform: translateX(-55%); }
}

/* THE REAL PATH, and it needs no guess at all.
   What we want is "slide left by however much the line overflows", which is
   (own width - box width). -100% is the line's own width and 100cqw is the
   box's, so calc(-100% + 100cqw) lands the end of the text flush with the
   right edge. Container query units are the only way to reach the parent's
   width from inside a transform.

   min(0px, ...) is the part that matters. On a line that already fits, that
   calc comes out POSITIVE and the text would drift to the right, which is
   how a topic measured at 227px in a 231px box was being animated at all.
   Clamping at zero makes the animation a genuine no-op whenever the line
   fits, at any column width, which is why this path ignores .long entirely
   and applies to every topic. The browser measures; nothing is estimated. */
@supports (width: 100cqw) {
  .card-topic { container-type: inline-size; }
  .card:hover .card-topic .ct-run {
    animation: ct-run 3s linear .3s infinite alternate;
  }
  @keyframes ct-run {
    from { transform: translateX(0); }
    to   { transform: translateX(min(0px, calc(-100% + 100cqw))); }
  }
}
@media (prefers-reduced-motion: reduce) {
  .card:hover .card-topic.long .ct-run { animation: none; }
  .card-topic { transition: none; }
}

/* Status is deliberately quiet. Around three quarters of the grid is in free
   chat at any moment, so a "Live now" pill on nearly every tile is noise that
   tells you nothing: free chat gets a breathing dot and no words at all, and
   the label is still there for screen readers. The two states worth knowing
   before you click get one word each. */
.card-live {
  position: absolute; top: 8px; left: 8px;
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 11px; font-weight: 700; letter-spacing: .03em;
}
.card-live i { width: 9px; height: 9px; border-radius: 50%; display: block; }
.card-live.free-chat i { background: var(--live); box-shadow: 0 0 0 0 rgba(34, 197, 94, .7); animation: pulse 2s infinite; }
.card-live.member-chat,
.card-live.private-chat {
  background: var(--dim); color: var(--white);
  padding: 3px 8px; border-radius: 999px; backdrop-filter: blur(2px);
}
.card-live.member-chat i { background: var(--gold); }
.card-live.private-chat i { background: var(--red); }

.card-new {
  position: absolute; top: 8px; right: 8px;
  background: var(--gold); color: var(--ink);
  font-size: 10.5px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
  padding: 3px 7px; border-radius: var(--radius-sm);
}

/* The promo unit is one more card in the grid rather than an advert bolted to
   the page. It is rendered at the creative's exact native size and never
   scaled: AWE injects the iframe without scrolling="no", so anything that does
   not fit its slot exactly grows scrollbars. */
.promo-card {
  /* ONE card slot, and the creative was changed to make that possible.
     It used to span 2 columns and 2 rows, which on a desktop meant roughly a
     536x420 box holding a 300x250 creative: the biggest thing on the page and
     mostly empty. That was forced, not chosen. A column is 224px (268px above
     1440px) and the creative was 300px WIDE, so it could never fit one column,
     and its 250px height then forced the second row.
     The creative is now 200x200, which fits a 224px column with room, so the
     tile is one column like everything around it. min-height carries the 200px
     creative in a row that is only about 168px tall at that breakpoint; at
     1440px and up a row is already ~206px, so the row barely moves at all.
     Below 720px columns drop to ~160px and 200 no longer fits, so it goes back
     to spanning two there. Never scale an AWE creative: pick a size the
     endpoint actually serves and give it exactly that box. */
  grid-column: span 2;
  grid-row: span 1;
  min-height: 208px;
  display: flex; align-items: center; justify-content: center;
  overflow: hidden; border-radius: var(--radius);
  /* Deliberately quieter than a card. Whatever space is left over should read
     as page rather than as an empty panel somebody forgot to fill. */
  border: 1px solid var(--line);
  background: rgba(0, 0, 0, .15);
}
/* From 720px up a column is at least 224px, which holds the 200px creative, so
   the tile becomes exactly one card slot. */
@media (min-width: 720px) { .promo-card { grid-column: span 1; } }
.promo-fit { line-height: 0; }
.promo-fit iframe { border: 0; overflow: hidden; }

.empty {
  grid-column: 1 / -1; padding: 40px 20px; text-align: center;
  color: var(--muted); background: var(--panel); border-radius: var(--radius);
}
.empty a { color: var(--gold); }

.sentinel { display: flex; justify-content: center; padding: 34px 0 8px; }
.spin {
  width: 26px; height: 26px; border-radius: 50%;
  border: 3px solid var(--line); border-top-color: var(--gold);
  animation: spin .8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

.pager { margin: 26px 0 0; text-align: center; }

/* --------------------------------------------------------------------------
   Recorded videos

   16:9 tiles against the model grid's 4:3, and that is the point rather than a
   detail: a visitor should be able to tell a recording from a live room at a
   glance, before reading a single word. The play glyph and the duration plate
   are the second and third signals. No green dot appears anywhere in here,
   because nothing in this section is live.
   -------------------------------------------------------------------------- */

.vgrid {
  display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
}
@media (min-width: 720px)  { .vgrid { grid-template-columns: repeat(auto-fill, minmax(268px, 1fr)); gap: 14px; } }
@media (min-width: 1440px) { .vgrid { grid-template-columns: repeat(auto-fill, minmax(310px, 1fr)); } }

.vcard {
  position: relative; display: block; overflow: hidden;
  aspect-ratio: 16 / 9;
  border-radius: var(--radius);
  border: 1px solid var(--line);
  background: var(--red-deep);
}
.vcard:hover { text-decoration: none; border-color: var(--red); }
.vcard-img { width: 100%; height: 100%; object-fit: cover; transition: transform .35s ease; }
.vcard:hover .vcard-img { transform: scale(1.04); }
@media (prefers-reduced-motion: reduce) { .vcard-img, .vcard:hover .vcard-img { transition: none; transform: none; } }

.vcard-veil {
  position: absolute; inset: auto 0 0 0; height: 56%;
  background: linear-gradient(180deg, transparent 0%, rgba(0, 0, 0, .55) 55%, rgba(0, 0, 0, .82) 100%);
  pointer-events: none;
}

/* A CSS triangle rather than an icon file or an inline SVG per card: the grid
   can hold two dozen of these and none of them needs its own markup. */
.vcard-play {
  position: absolute; top: 50%; left: 50%;
  width: 52px; height: 52px; margin: -26px 0 0 -26px;
  border-radius: 50%;
  background: rgba(0, 0, 0, .45);
  border: 2px solid rgba(255, 255, 255, .85);
  transition: background .2s ease, border-color .2s ease;
}
.vcard-play::after {
  content: ''; position: absolute; top: 50%; left: 54%;
  transform: translate(-50%, -50%);
  border-style: solid; border-width: 9px 0 9px 15px;
  border-color: transparent transparent transparent #fff;
}
.vcard:hover .vcard-play { background: var(--gold); border-color: var(--gold); }
.vcard:hover .vcard-play::after { border-left-color: var(--ink); }

.vcard-time {
  position: absolute; right: 8px; bottom: 8px;
  background: rgba(0, 0, 0, .68); color: #fff;
  font-size: 11.5px; font-weight: 700; font-variant-numeric: tabular-nums;
  padding: 2px 7px; border-radius: var(--radius-sm);
}
.vcard-hd {
  position: absolute; top: 8px; right: 8px;
  background: var(--gold); color: var(--ink);
  font-size: 10.5px; font-weight: 700; letter-spacing: .05em;
  padding: 2px 6px; border-radius: var(--radius-sm);
}
.vcard-bar { position: absolute; left: 0; right: 0; bottom: 0; padding: 9px 11px 9px; }
.vcard-title {
  display: block; font-weight: 600; font-size: 14px; line-height: 1.3; color: var(--white);
  /* Two lines then ellipsis: API titles run long and a third line would push
     into the duration plate. */
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; padding-right: 46px;
}

/* --------------------------------------------------------------------------
   The row inside the live grid

   A full width band dropped into the grid a couple of rows down, rather than
   a section further down the page. grid-column: 1 / -1 makes it span whatever
   the column count happens to be, so it stays flush at every breakpoint and
   needs no media query of its own.

   It carries its own top and bottom rule and a line of copy, because the one
   thing it must never do is read as more women who are on cam. Everything
   above and below it in the grid is live; this band is the exception and it
   has to announce itself.
   -------------------------------------------------------------------------- */

/* The band carries the boundary on its own edges, with no closing sentence.
   A hairline opens it and a gold rule closes it, so the eye reads "something
   else starts here" and "the cams resume here" without being told either in
   words. The bottom rule is the heavier of the two on purpose: leaving the
   band matters more than entering it, because that is the moment somebody
   decides whether the page has ended. */
.grid-row {
  grid-column: 1 / -1;
  margin: 18px 0;
  padding: 20px 0 22px;
  border-top: 1px solid var(--line);
  border-bottom: 2px solid var(--gold);
}
/* The join variant holds live women, so it is allowed to feel like part of the
   grid: a gold edge rather than a plain rule, because it is the better row. It
   keeps the gold underline for the same reason as above. */
.grid-row.join {
  border-top: 0;
  border-left: 4px solid var(--gold);
  border-bottom: 2px solid var(--gold);
  padding: 4px 0 18px 18px;
  background: rgba(0, 0, 0, .16);
  border-radius: var(--radius);
}
.row-head { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.row-h { font-size: clamp(18px, 2vw, 23px); margin: 0; }
.row-all { flex: 0 0 auto; color: var(--gold); font-size: 13.5px; font-weight: 600; }
.row-note { margin: 5px 0 14px; font-size: 13.5px; color: var(--muted-2); }

/* The nested grids inside the band size themselves, so they need nothing here
   beyond a reset of the bottom margin the band already provides. */
.grid-row .grid,
.grid-row .vgrid { margin-bottom: 16px; }

/* She is live AND she has a recording. Sits in the card bar next to her age
   rather than over the picture: the top corners already carry the status badge
   and the New plate, and a fourth thing floating on her is one too many. */
.card-vid {
  margin-left: auto; flex: 0 0 auto;
  font-size: 10px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink); background: var(--gold);
  padding: 2px 6px; border-radius: var(--radius-sm);
}

/* The strip under a live grid. Kept for a caller that wants a standalone band;
   the home and category pages use .grid-row above instead. Set apart from the
   cams with a rule and a line of copy for the same reason. */
.vstrip { margin-top: 44px; padding-top: 30px; border-top: 1px solid var(--line); }
.vstrip-head { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.vstrip-head .h2 { margin: 0; }
.vstrip-all { color: var(--gold); font-size: 14px; font-weight: 600; flex: 0 0 auto; }
.vstrip-note { margin: 6px 0 16px; font-size: 14px; color: var(--muted-2); }

/* Rail entry for the section, set apart from the category list above it. */
.rail-item.vid .rail-name { color: var(--gold); }

/* The "she is live after all" banner on a video page. */
.live-note {
  display: flex; align-items: center; gap: 10px;
  margin: 0 0 26px; padding: 13px 17px; border-radius: var(--radius);
  background: var(--panel-2); border: 1px solid var(--line);
  border-left: 4px solid var(--live);
  font-size: 15px;
}

/* --------------------------------------------------------------------------
   Buttons. Gold means "this is the click", and nothing else on the site is
   gold, so the eye has one place to land on every page.
   -------------------------------------------------------------------------- */

.btn {
  display: inline-block; padding: 11px 22px; border-radius: var(--radius-sm);
  font-weight: 700; font-size: 15px; letter-spacing: .01em;
  background: var(--panel-2); color: var(--white); border: 1px solid var(--line);
}
.btn:hover { background: var(--red); text-decoration: none; }
.btn.gold { background: var(--gold); color: var(--ink); border-color: var(--gold); }
.btn.gold:hover { background: var(--gold-hi); border-color: var(--gold-hi); }
.btn.big { padding: 14px 30px; font-size: 17px; }
/* The action bar button. Deliberately larger than any other control on the
   site: it is the only thing on the model page that earns money, and it lost
   its explanatory caption, so it has to carry the whole message on its own. */
.btn.huge { padding: 16px 40px; font-size: 19px; box-shadow: 0 8px 22px rgba(0, 0, 0, .32); }

/* --------------------------------------------------------------------------
   Category hub
   -------------------------------------------------------------------------- */

.cat-grid { display: grid; gap: 10px; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); }
.cat-card {
  display: flex; flex-direction: column; gap: 3px;
  padding: 14px 16px; border-radius: var(--radius);
  background: var(--panel-2); border: 1px solid var(--line);
  border-left: 3px solid var(--red);
}
.cat-card:hover { background: var(--red); text-decoration: none; }
.cat-grid.soft .cat-card { background: var(--panel); border-left-color: var(--line); }
.cat-name { font-weight: 700; font-size: 15.5px; }
.cat-count { font-size: 12.5px; color: var(--muted-2); font-variant-numeric: tabular-nums; }

/* --------------------------------------------------------------------------
   Feature cards and Q&A
   -------------------------------------------------------------------------- */

.features {
  display: grid; gap: 12px; margin: 44px 0 0;
  /* 300 rather than 258: at a typical desktop content width 258 yields four
     columns and six cards then break 4+2, which reads as a row that ran out.
     300 yields three and they break 3+3. */
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
}
.feature {
  padding: 18px 20px; border-radius: var(--radius);
  background: var(--panel); border: 1px solid var(--line);
}
.feat-icon { color: var(--gold); margin-bottom: 9px; }
.feat-title { font-size: 17.5px; margin: 0 0 6px; }
.feat-text { margin: 0; font-size: 14.5px; color: var(--muted); line-height: 1.55; }

.qa { margin-top: 44px; padding-top: 30px; border-top: 1px solid var(--line); }
.qa-grid { display: grid; gap: 12px; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }
.qa-item {
  padding: 16px 20px; border-radius: var(--radius);
  background: var(--panel); border: 1px solid var(--line);
}
.qa-q { font-size: 17px; margin: 0 0 7px; }
.qa-a { margin: 0; color: var(--muted); font-size: 14.5px; line-height: 1.6; }
.qa-a a { color: var(--gold); }

/* --------------------------------------------------------------------------
   The model page

   The player is the page. It is the first element in <main>, it is sized to
   whatever the viewport has left after the bar, and nothing is allowed above
   it. The rail is suppressed here for the same reason.
   -------------------------------------------------------------------------- */

body.is-model .wrap { max-width: none; margin: 0; padding: 0; }

.stage {
  position: relative; width: 100%;
  /* Full screen height minus the bar, with a floor for short landscape phones
     and a ceiling so an ultrawide monitor does not stretch the picture. svh
     rather than vh, so mobile browser chrome cannot push the player off
     screen. */
  height: clamp(320px, calc(100svh - var(--hdr)), 1100px);
  background: #000;
  border-bottom: 2px solid var(--red);
}
/* The embed script replaces #object_container with its own #object_container_0
   carrying inline width:100%, height:100% and display:inline-block, then drops
   an iframe inside it. The inline styles are why these need !important, and
   inline-block is why they need display:block: it would otherwise leave a
   baseline gap under the player. */
.stage > div,
.stage iframe,
.stage object,
.stage embed,
.stage video {
  width: 100% !important;
  height: 100% !important;
  border: 0;
  display: block !important;
}
/* The player has no overlay of ours over it. See the note in video.php:
   the LiveJasmin player already carries her live state, so a second badge
   saying the same thing would be two things competing to make one point. */

.stage-fallback {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 16px; padding: 24px; text-align: center;
}
.stage-fallback img { max-height: 62%; width: auto; border-radius: var(--radius-sm); }
.stage-fallback a { color: var(--gold); }

.under { max-width: 1320px; margin: 0 auto; padding: 26px 20px 60px; }

/* ---- The action bar -------------------------------------------------------
   Sticks directly under the site bar once the player has scrolled away, so her
   name, her state, her rate and the button stay reachable from anywhere on the
   page. This is the one structural decision on the model page: with the chat
   now floating inside the player, the visitor spends real time below the fold,
   and a call to action that only exists at the top is a call to action he
   never sees again.
   -------------------------------------------------------------------------- */

.actionbar {
  position: sticky; top: var(--hdr); z-index: 30;
  background: var(--red-deep);
  border-bottom: 1px solid rgba(0, 0, 0, .3);
  box-shadow: 0 10px 26px rgba(0, 0, 0, .35);
}
.ab-in {
  max-width: 1320px; margin: 0 auto;
  display: flex; flex-wrap: wrap; align-items: center; gap: 14px 26px;
  padding: 12px 20px;
}
.ab-id { flex: 1 1 300px; min-width: 0; }
.ab-name {
  font-size: clamp(22px, 2.6vw, 32px); line-height: 1.1;
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 10px;
}
/* The keyword half of the H1, deliberately quiet. It is inside the heading so
   it counts, and at 13px uppercase so it reads as a label rather than as
   somebody shouting a search term at the top of the page. */
.ab-tail {
  font-family: var(--sans); font-size: 13px; font-weight: 600;
  text-transform: uppercase; letter-spacing: .1em; color: rgba(255, 255, 255, .6);
}
.ab-meta {
  margin: 8px 0 0;
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px 15px;
  font-size: 13.5px;
}
.ab-fact { color: rgba(255, 255, 255, .78); font-weight: 600; }
.ab-fact.star::before { content: '\2605'; color: var(--gold); margin-right: 5px; }
.ab-fact.rate { color: var(--white); }
.ab-fact.rate em { font-style: normal; font-weight: 500; color: rgba(255, 255, 255, .62); }
.ab-in .btn { flex: 0 0 auto; }

/* ---- Her room topic -------------------------------------------------------
   Promoted out of a one line caption into a block of its own. On this site it
   is a tip menu far more often than not, which makes it the most actionable
   thing she writes, and it is the only gold-edged element below the player.
   -------------------------------------------------------------------------- */

.tipmenu {
  margin: 0 0 30px;
  padding: 18px 22px;
  border-radius: var(--radius);
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-left: 4px solid var(--gold);
}
.tip-h {
  font-family: var(--sans); font-size: 11.5px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .12em; color: var(--gold);
  margin: 0 0 8px;
}
.tip-body { margin: 0; font-size: 17px; line-height: 1.5; color: var(--white); }

/* ---- The dossier: her words left, the facts right ------------------------
   A stacked full width column gave a 74 character paragraph the same measure
   as a nine row fact table and buried the facts under the reading. Splitting
   them lets each be the width it wants to be.
   -------------------------------------------------------------------------- */

.dossier { display: grid; gap: 30px; align-items: start; }

@media (min-width: 980px) {
  .dossier { grid-template-columns: minmax(0, 1fr) 336px; gap: 44px; }
  /* Sticky so the facts stay beside whatever paragraph he is on rather than
     being scrolled past once. The offset has to clear the site bar AND the
     action bar; --abar is that bar's height, which is not a fixed number
     because her name wraps at some widths, so it is an approximation with a
     max-height and a scroll of its own to absorb the error. */
  .dos-side {
    position: sticky; top: calc(var(--hdr) + var(--abar) + 18px);
    max-height: calc(100vh - var(--hdr) - var(--abar) - 36px);
    overflow-y: auto; overscroll-behavior: contain;
    /* Keeps the thumb off the panels' right border. The rail does not need
       this because its rows are full bleed and carry 18px of their own
       padding, so a 6px bar lands inside that and never touches the count
       badge; these panels have a visible edge for it to sit on. */
    padding-right: 8px;
  }
}

.blk { margin-bottom: 32px; }
.tail-go { margin: 32px 0 0; display: flex; flex-wrap: wrap; align-items: center; gap: 14px 20px; }

/* The internal link beside the outbound button on a video page. Deliberately
   not a second button: one gold thing per view, and this is the quieter of the
   two offers. */
.tail-alt { color: var(--gold); font-size: 14.5px; font-weight: 600; }

.panel {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 16px 18px; margin-bottom: 14px;
}
.panel-h {
  font-family: var(--sans); font-size: 11.5px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .12em; color: var(--muted-2);
  margin: 0 0 12px;
}

.facts { list-style: none; margin: 0; padding: 0; }
.facts li {
  display: flex; justify-content: space-between; align-items: baseline; gap: 14px;
  padding: 8px 0; border-bottom: 1px solid var(--line);
}
.facts li:last-child { border-bottom: 0; padding-bottom: 0; }
.facts li:first-child { padding-top: 0; }
.f-k { flex: 0 0 auto; color: var(--muted-2); font-size: 13.5px; }
.f-v { font-weight: 600; font-size: 14.5px; text-align: right; text-transform: capitalize; }
/* The rate is the number he actually wants, so it is the one thing in the
   sidebar allowed to be gold. */
.f-v.big { font-size: 20px; color: var(--gold); }
.f-v.big em { font-style: normal; font-size: 12px; font-weight: 500; color: var(--muted-2); text-transform: none; }
.f-v.star::before { content: '\2605'; color: var(--gold); margin-right: 5px; }
.f-v.yes { color: var(--live); }
.f-v.no { color: var(--muted-2); font-weight: 500; }

.chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 11px; border-radius: 999px;
  background: var(--panel-2); border: 1px solid var(--line);
  font-size: 13px; font-weight: 600; color: var(--white);
}
.chip.link:hover { background: var(--red); text-decoration: none; }
.chip.star::before { content: '\2605'; color: var(--gold); }
.chip.live i { width: 8px; height: 8px; border-radius: 50%; background: var(--muted-2); }
.chip.live.free-chat i { background: var(--live); }
.chip.live.member-chat i { background: var(--gold); }
.chip.live.private-chat i { background: var(--red); }

.offline-note {
  margin: 0 0 22px; padding: 12px 16px; border-radius: var(--radius);
  background: var(--panel); border-left: 3px solid var(--gold); color: var(--muted);
  font-size: 14.5px;
}
.likes { display: grid; gap: 16px; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
.likes-col { padding: 16px 20px; border-radius: var(--radius); background: var(--panel); border: 1px solid var(--line); }
.likes-col.on { border-left: 3px solid var(--live); }
.likes-col.off { border-left: 3px solid var(--red); }

/* Chips are for things you can CLICK. Only the category rows use them now. */
.chips { display: flex; flex-wrap: wrap; gap: 7px; margin: 0; }
/* The sidebar runs a chip row in a 336px column, so it shrinks there. */
.chips.tight { gap: 6px; }
.chips.tight .chip { font-size: 12.5px; padding: 4px 9px; }

/* --------------------------------------------------------------------------
   Tag lists: static labels, and deliberately NOT chips

   Her willingness list, her appearance list and a video's tag list are values
   from the feed with nothing behind them. They used to be rendered as pills,
   identical to the linked category chips sitting two blocks away on the same
   page, so they read as two dozen buttons and did nothing when tapped.

   Plain text with a separator says "label" with no ambiguity at all, and it
   fits far more of them in the same space, which matters when a video carries
   twenty four tags. If any of these ever becomes a real link it should become
   a .chip.link and move into a .chips row, rather than this growing a hover.
   -------------------------------------------------------------------------- */

.taglist { margin: 0; line-height: 1.95; color: var(--muted); }
.tag {
  font-size: 14.5px; font-weight: 500;
  text-transform: capitalize;
  white-space: nowrap;
}
/* Separator between items rather than after each, so no trailing dot.
   The margins are asymmetric on purpose. The template prints a real space
   between the spans, which is what gives the line somewhere to break: the tags
   are nowrap and adjacent inline boxes with no whitespace between them offer
   NO wrap opportunity at all, so without it a long tag list ran off the side of
   the page and put a horizontal scrollbar on the whole document. That space
   collapses to about 4px and sits to the left of this dot, so the left margin
   is 4 smaller to keep the gaps even either side. */
.tag + .tag::before {
  content: '\00B7';
  margin: 0 8px 0 4px;
  color: rgba(255, 255, 255, .32);
  font-weight: 400;
}
.taglist.tight { line-height: 1.85; }
.taglist.tight .tag { font-size: 13px; }
.taglist.tight .tag + .tag::before { margin: 0 6px 0 2px; }

.related { margin-top: 44px; padding-top: 32px; border-top: 1px solid var(--line); }

/* --------------------------------------------------------------------------
   Static and error pages
   -------------------------------------------------------------------------- */

.page { max-width: 74ch; }
.page .h1 { margin-bottom: 16px; }

/* --------------------------------------------------------------------------
   Footer
   -------------------------------------------------------------------------- */

.foot {
  background: var(--red-deep);
  border-top: 1px solid rgba(0, 0, 0, .3);
  padding: 30px 20px 34px;
}
.foot-in { max-width: 1080px; margin: 0 auto; text-align: center; }
.foot-nav { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px 24px; margin-bottom: 16px; }
.foot-nav a { font-size: 14px; font-weight: 600; color: rgba(255, 255, 255, .9); }
.foot-copy { margin: 0; font-size: 12.5px; color: rgba(255, 255, 255, .5); }

/* --------------------------------------------------------------------------
   Phones. The bar and the mark come down together, and "All girls" leaves the
   nav so the logo and "Categories" still fit a 375px screen. Nothing is lost
   by that: the logo already links home.
   -------------------------------------------------------------------------- */

@media (max-width: 640px) {
  :root { --hdr: 62px; }
  .top-in { gap: 12px; padding: 0 12px; }
  .brand img { height: 38px; }
  /* The bar swaps the pills for the burger. Nothing is dropped: the old rule
     here hid the home link entirely to make room, on exactly the screens where
     the category rail is also hidden, which left the grid reachable only by
     the logo. */
  .top-nav { gap: 10px; }
  .nav-links { display: none; }
  .menu { display: block; }
  .wrap { padding: 16px 12px 44px; }
  .under { padding: 20px 12px 44px; }

  /* The action bar goes to two stacked rows and the button takes the full
     width. It is still sticky: on a phone the player fills the screen, so
     everything below it is scrolled to, and the button has to come along. */
  .ab-in { padding: 10px 12px; gap: 10px; }
  .ab-name { font-size: 21px; }
  .ab-tail { font-size: 11.5px; }
  .ab-meta { gap: 6px 12px; font-size: 12.5px; }
  .ab-in .btn { flex: 1 1 100%; text-align: center; }

  /* ---- 56px of page under the player, and that is the whole fix ------
     An iframe swallows touch on iOS: a finger landing inside a cross origin
     frame does not scroll the page behind it, and a video player makes it
     worse by calling preventDefault on touchmove for its own seek gestures.
     Sized to the exact viewport there was nothing else on screen to drag.

     It cannot be fixed from inside the iframe, so the page needs a strip of
     itself visible. 56px shows the top edge of the action bar: enough to drag
     from, and enough to say there is more down there. The player still has
     about 93% of the screen, which is what "full screen" means to an eye.

     Two more elaborate versions were built and deleted before this one. A
     132px cut left the whole action bar showing and lost too much picture. A
     floating chevron anchor kept 100% of the screen but cost fifty lines of
     CSS, markup on two templates, a locale string and an aria-label, to add a
     control nobody asked for. This is one declaration.

     Worth knowing: the page was probably never strictly stuck. The 62px site
     bar is not an iframe and drags fine. The real problem was that nobody
     thinks to drag the header, so the fix is about making the page look
     scrollable, not about adding a way to scroll it. */
  .stage { height: calc(100svh - var(--hdr) - 56px); }

  .btn.huge { padding: 14px 20px; font-size: 17px; }

  .tipmenu { padding: 14px 16px; }
  .tip-body { font-size: 15.5px; }
  .tail-go .btn { display: block; text-align: center; }
  /* Below this width the promo takes the whole row rather than spanning two
     columns, and that is a guard as much as a layout choice.
     `span 2` is what created an implicit third column at 320px once, which in
     turn broke `grid-column: 1 / -1` on the video row, because `-1` only ever
     resolves to the end of the EXPLICIT grid. `1 / -1` here cannot create an
     implicit track no matter how the column count moves, so the two irregular
     items in this grid can never fight again.
     `grid-row: auto` because the creative is 300x250 and a full width row on a
     phone is already about 296px: one row is the right shape now, and spanning
     two would leave dead space under it. min-height carries the 250px creative,
     which would otherwise be clipped by the tile's overflow:hidden. */
  .promo-card {
    grid-column: 1 / -1;
    grid-row: auto;
    /* Matches the 200px creative plus its border. This was 262px while the
       creative was 300x250; leaving it there would have left a 60px band of
       nothing under the unit on every phone. */
    min-height: 208px;
  }
}
