/* ══ THE SERVICE CARD ═══════════════════════════════════  7 Sep 2026 ══════
   Sir: the saved page should have the same cards as the results page, and they
   should function the same.

   THIS SHEET WAS WRITTEN FROM THE MEASUREMENT, NOT BY MOVING RULES. The card
   on `results.html` is built through several layers that supersede each other
   in place: the title is declared once in the reading face at 15.5px and again
   two lines later in the label face at 13px; the well is given a 14px radius
   and taken back to 0 eighty lines down; the stars are drawn at 72 by 12 and
   redrawn at 60 by 10. Cutting and pasting those layers into a shared file
   would have carried three dead declarations and, on any page whose own sheet
   loads in a different order, three different answers.

   So every value here is the value the shipped card COMPUTES, read off the
   rendered element at 1400 with `getComputedStyle`, and the sheet is proved by
   diffing 35 elements of the saved page's card against 35 of the results
   page's. The superseded declarations are simply gone.

   THE STAR MASK IS THE ONE THING COPIED CHARACTER FOR CHARACTER, because it is
   a drawing and a drawing is never re-derived.

   WHAT THIS SHEET DOES NOT OWN: the grid the cards sit in. Three across with a
   46px row gap is the results column's decision, and a page that shows saved
   work in two bands wants its own. A component sheet that sets its container's
   columns cannot be used twice.

   TOKENS ONLY. Every colour and face here resolves to a variable the loading
   page declares. There is no hex in this file.
   ═══════════════════════════════════════════════════════════════════════ */

.tc {
  position: relative; display: block; text-decoration: none;
  color: var(--ink); cursor: pointer;
  background: none; border: 0; border-radius: 0; overflow: visible;
}

/* ── the well ──────────────────────────────────────────────────────────
   Square, and square-cornered, so a row reads as squares rather than as
   nearly-squares. */
.tc .result-img {
  position: relative; overflow: hidden;
  aspect-ratio: 1 / 1; border-radius: 0; min-height: 0;
  background: var(--cream-2);
}
.tc .result-img img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; display: block; opacity: 0;
  filter: saturate(.82) contrast(.97);
  transition: opacity 420ms ease, transform 420ms cubic-bezier(.25, .1, .25, 1);
}
.tc .result-img img.active { opacity: 1; }
.tc:hover .result-img img.active { transform: scale(1.05); }

/* A LISTING WITH NO PHOTOGRAPHS ON IT YET. The frame stays so the row does not
   stagger, and the ground says so plainly rather than borrowing a stock
   photograph of another trade's work. */
.tc .result-img.is-nopix {
  display: grid; place-items: center;
  background: color-mix(in srgb, var(--navy) 88%, var(--cream-2));
}
.tc .result-img.is-nopix::after {
  content: 'No photographs'; font-family: var(--sans); font-size: 11px;
  font-weight: 500; letter-spacing: .16em; text-transform: uppercase;
  color: rgba(250, 248, 243, .62);
}

.tc .slide-dots {
  position: absolute; bottom: 14px; left: 50%; transform: translateX(-50%);
  display: flex; gap: 5px; z-index: 2; opacity: 0; transition: opacity 250ms;
}
.tc:hover .slide-dots { opacity: 1; }
.tc .slide-dot { width: 6px; height: 6px; border-radius: 50%;
  background: rgba(255, 255, 255, .5); transition: all 250ms; }
.tc .slide-dot.active { width: 16px; border-radius: 3px; background: #fff; }

/* The placement chip sits in the corner of the photograph, which is where a
   badge belongs: on the thing it describes. */
/* `line-height: 0` because the flag is a box round an inline chip, so without
   it the box inherits the loading page's line box and measures 21px on a page
   whose body is 16px and 20px on one whose body is 14px. Caught by diffing the
   two cards element for element, not by looking at them. */
.tc-flag { position: absolute; top: 12px; left: 12px; z-index: 3; line-height: 0; }
.tc-chip { display: inline-flex; align-items: center; padding: 5px 11px;
  border-radius: 999px; font-family: var(--sans); font-size: 9px;
  font-weight: 500; letter-spacing: .14em; text-transform: uppercase;
  line-height: 1; white-space: nowrap; }
.tc-chip-navy { background: var(--navy); color: var(--cream); }
/* Plain, NOT green. Green here means a thing checked, and a placement is not
   a check of anything. */
.tc-chip-plain { background: rgba(250, 248, 243, .93); color: var(--navy);
  box-shadow: inset 0 0 0 1px var(--line-2);
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px); }

/* ── saving. no chrome, and it appears on hover ────────────────────────
   The control writes to the record. See `hyyryn-card.js`. */
.tc .result-fav {
  position: absolute; top: 12px; right: 12px; z-index: 3;
  width: 30px; height: 30px;
  display: flex; align-items: center; justify-content: center;
  background: none; border: 0; cursor: pointer;
  opacity: 0; transition: opacity 200ms ease, transform 180ms ease;
}
.tc:hover .result-fav, .tc .result-fav:focus-visible { opacity: 1; }
/* A SAVED CARD SHOWS IT WITHOUT BEING ASKED. Hiding the one state the control
   exists to report until the pointer arrives is how a page ends up unable to
   say whether a thing is saved. */
.tc .result-fav.active { opacity: 1; }
.tc .result-fav:hover { transform: scale(1.12); }
.tc .result-fav svg { width: 19px; height: 19px; fill: none; stroke: #fff;
  stroke-width: 1.8; filter: drop-shadow(0 1px 3px rgba(10, 22, 40, .5)); }
.tc .result-fav.active svg { fill: var(--gold-soft); stroke: var(--gold-soft); }

/* ── the caption ───────────────────────────────────────────────────────
   The title is set in the wordmark's own treatment scaled up, so it belongs
   with the mark, the labels and the pills. The reading face keeps the trade
   names, the places and the numbers. */
.tc-txt { position: relative; }
.tc-cap { display: flex; align-items: flex-start; justify-content: space-between;
  gap: 14px; margin-top: 14px; }
.tc-cap h3 { margin: 0; font-family: var(--sans); font-weight: 500;
  font-size: 13px; letter-spacing: .20em; text-transform: uppercase;
  color: var(--navy); line-height: 1.35; }
.tc:hover .tc-cap h3 { text-decoration: underline; text-underline-offset: 3px; }
.tc-p { font-family: var(--serif); font-size: 16px; color: var(--navy);
  white-space: nowrap; font-variant-numeric: tabular-nums; }
.tc-p small { display: block; font-family: var(--sans); font-size: 8.5px;
  font-weight: 500; letter-spacing: .13em; text-transform: uppercase;
  color: var(--muted); text-align: right; margin-bottom: 3px; }

/* ── the reveal, and it asks for half a second ─────────────────────────
   The trigger is the TEXT STRIP, not the card: crossing a photograph on the
   way somewhere else should not open anything. The delay is a
   transition-delay rather than a timer, because CSS already knows how long the
   pointer has been inside a rule's subject and a timer would have to be
   cleared on leave, on scroll, on blur and on the card being filtered out.
   Opening waits; closing does not, which is what every hover-intent control
   wants. A keyboard user gets no delay: there is no accidental focus to
   protect against, so waiting would only be a wait. */
.tc-more { max-height: 0; overflow: hidden; opacity: 0;
  transition: max-height 320ms cubic-bezier(.16, 1, .3, 1),
              opacity 200ms cubic-bezier(.25, .1, .25, 1); }
.tc-hint { display: block; height: 20px; overflow: hidden; opacity: 1;
  transition: opacity 200ms cubic-bezier(.25, .1, .25, 1),
              height 260ms cubic-bezier(.16, 1, .3, 1); }
.tc-hint svg { display: block; width: 13px; height: 13px; margin-top: 5px;
  fill: none; stroke: var(--muted); stroke-width: 1.5;
  stroke-linecap: round; stroke-linejoin: round; }

@media (hover: hover) and (pointer: fine) {
  .tc-txt:hover .tc-more { max-height: 180px; opacity: 1; transition-delay: 500ms; }
  .tc-txt:hover .tc-hint { height: 0; opacity: 0; transition-delay: 500ms; }
  .tc-txt:focus-within .tc-more { max-height: 180px; opacity: 1; transition-delay: 0ms; }
  .tc-txt:focus-within .tc-hint { height: 0; opacity: 0; transition-delay: 0ms; }
}
/* Touch has no hover, so the panel is simply open and the chevron would be a
   promise about nothing. */
@media (hover: none) {
  .tc-more { max-height: 180px; opacity: 1; }
  .tc-hint { display: none; }
  /* rp157 (17 Oct 2026, the launch-day pass): and the bookmark that saves a card showed only on hover, so on a phone
     it was never there to press */
  .tc .result-fav { opacity: 1; }
}

.tc-who { display: flex; align-items: center; gap: 12px; margin-top: 10px; }
.tc-who-t { min-width: 0; }
.tc-face { width: 38px; height: 38px; flex: none; border-radius: 50%;
  object-fit: cover; filter: saturate(.82) contrast(.97);
  box-shadow: 0 0 0 1px var(--line), 0 1px 3px rgba(10, 22, 40, .20); }
/* A NAMED PERSON WITHOUT A LIKENESS GETS INITIALS, NOT A STOCK FACE. Same
   disc, same ring, the display face inside it. */
.tc-face--ini { display: grid; place-items: center; background: var(--cream-2);
  font-family: var(--serif-display); font-size: 15px; color: var(--navy);
  letter-spacing: .02em; }
.tc-place { margin: 0; font-family: var(--serif); font-size: 14px; color: var(--ink); }

/* FIVE STARS FROM ONE MASK. The empty ground is the element, the filled bar is
   a child at --pc wide, and the star shapes mask the PARENT, so both layers
   clip in one pass. That is what buys a true fractional fill: 4.7 is 94 per
   cent of the bar, not a rounded-up fifth star. */
.tc-rate { display: flex; align-items: center; gap: 8px; margin-top: 4px; }
.tc-stars { position: relative; flex: none; width: 60px; height: 10px; background: var(--line);
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 60 10' fill='black'%3E%3Cpath d='M5 .58 6.375 3.79 9.83 4.07 7.19 6.33 8 9.71 5 7.9 2 9.71 2.81 6.33 .17 4.07 3.63 3.79Z' transform='translate(0 0)'/%3E%3Cpath d='M5 .58 6.375 3.79 9.83 4.07 7.19 6.33 8 9.71 5 7.9 2 9.71 2.81 6.33 .17 4.07 3.63 3.79Z' transform='translate(12.5 0)'/%3E%3Cpath d='M5 .58 6.375 3.79 9.83 4.07 7.19 6.33 8 9.71 5 7.9 2 9.71 2.81 6.33 .17 4.07 3.63 3.79Z' transform='translate(25 0)'/%3E%3Cpath d='M5 .58 6.375 3.79 9.83 4.07 7.19 6.33 8 9.71 5 7.9 2 9.71 2.81 6.33 .17 4.07 3.63 3.79Z' transform='translate(37.5 0)'/%3E%3Cpath d='M5 .58 6.375 3.79 9.83 4.07 7.19 6.33 8 9.71 5 7.9 2 9.71 2.81 6.33 .17 4.07 3.63 3.79Z' transform='translate(50 0)'/%3E%3C/svg%3E") left center/60px 10px no-repeat;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 60 10' fill='black'%3E%3Cpath d='M5 .58 6.375 3.79 9.83 4.07 7.19 6.33 8 9.71 5 7.9 2 9.71 2.81 6.33 .17 4.07 3.63 3.79Z' transform='translate(0 0)'/%3E%3Cpath d='M5 .58 6.375 3.79 9.83 4.07 7.19 6.33 8 9.71 5 7.9 2 9.71 2.81 6.33 .17 4.07 3.63 3.79Z' transform='translate(12.5 0)'/%3E%3Cpath d='M5 .58 6.375 3.79 9.83 4.07 7.19 6.33 8 9.71 5 7.9 2 9.71 2.81 6.33 .17 4.07 3.63 3.79Z' transform='translate(25 0)'/%3E%3Cpath d='M5 .58 6.375 3.79 9.83 4.07 7.19 6.33 8 9.71 5 7.9 2 9.71 2.81 6.33 .17 4.07 3.63 3.79Z' transform='translate(37.5 0)'/%3E%3Cpath d='M5 .58 6.375 3.79 9.83 4.07 7.19 6.33 8 9.71 5 7.9 2 9.71 2.81 6.33 .17 4.07 3.63 3.79Z' transform='translate(50 0)'/%3E%3C/svg%3E") left center/60px 10px no-repeat; }
.tc-stars i { position: absolute; inset: 0 auto 0 0; width: var(--pc); background: var(--gold); }
.tc-rate b { font-family: var(--serif); font-size: 13px; color: var(--navy);
  letter-spacing: .01em; font-variant-numeric: tabular-nums; }
/* rp113: a score always carries its count (assets/hyyryn-score.js) */
.tc-rate .tc-n { font-family: var(--sans); font-weight: 400; font-size: 11.5px; color: var(--muted, #636976); }

/* THE COMPONENT EMITS IT, SO THE COMPONENT DECLARES IT. The rating carries
   "out of 5" for a screen reader, and on a page that had never happened to
   define `.sr-only` those four words rendered at full size next to the number.
   A component that relies on a utility class its host may not have is a
   component that reads correctly on the page it was written for and nowhere
   else. */
.tc .sr-only {
  position: absolute; width: 1px; height: 1px; margin: -1px;
  padding: 0; border: 0; overflow: hidden; white-space: nowrap;
  clip: rect(0 0 0 0); clip-path: inset(50%);
}

.tc-acts { display: flex; gap: 10px; margin-top: 14px; }
.tc-pill { height: 30px; padding: 0 13px; border-radius: 999px;
  border: 1px solid var(--line-2); background: transparent;
  font-family: var(--sans); font-size: 10px; font-weight: 500;
  letter-spacing: .1em; text-transform: uppercase; color: var(--navy);
  display: inline-flex; align-items: center;
  transition: background 160ms cubic-bezier(.25, .1, .25, 1); }
.tc:hover .tc-pill:hover { background: rgba(10, 22, 40, .05); }

/* §23. No tween, and the open state still declared. */
@media (prefers-reduced-motion: reduce) {
  .tc .result-img img { transition: none; }
  .tc:hover .result-img img.active { transform: none; }
  .tc-more, .tc-hint { transition: none; }
  .tc-txt:hover .tc-more, .tc-txt:focus-within .tc-more { transition-delay: 0ms; }
  .tc-txt:hover .tc-hint, .tc-txt:focus-within .tc-hint { transition-delay: 0ms; }
}

/* ══ TAKEN DOWN ═══════════════════════════════════════════  7 Sep 2026 ══════
   Sir: show available and unavailable, and grey out the unavailable ones.

   GREY IS NOT A COLOUR HERE, IT IS THE ABSENCE OF ONE. `saturate(0)` on the
   photograph and the face, and the type steps back to the muted token. Nothing
   is repainted, so nothing has to be un-repainted when the listing comes back,
   and the card is still legible: this is a listing the reader chose once and
   may want again, not a mistake to be hidden.

   IT IS SAID IN WORDS AS WELL. Colour alone is not a state: a reader who
   cannot see the difference between this card and the one above it gets the
   chip on the photograph and the line under the title, which say the same
   thing the grey does.

   THE HEART STAYS LIVE. Unsaving something that is no longer listed is the
   one thing a reader is most likely to want to do here, so the control that
   does it is not disabled along with everything else. */
.tc--off .result-img img,
.tc--off .tc-face { filter: saturate(0) contrast(.97) brightness(1.02); }
.tc--off .result-img { background: var(--cream-2); }
.tc--off .tc-cap h3,
.tc--off .tc-p { color: var(--muted); }
/* NOT `--line-2`. Stepping the "FROM" label back a second time put 8.5px of
   #D8CFB8 on cream at 1.5:1, which is a label nobody can read rather than a
   quiet one. Grey here is desaturation, and desaturation has a floor. */
.tc--off .tc-p small { color: var(--muted); }
.tc--off .tc-place { color: var(--muted); }
.tc--off .tc-stars i { background: var(--line-2); }
.tc--off .tc-pill { color: var(--muted); border-color: var(--line); }
.tc--off:hover .result-img img.active { transform: none; }

.tc-off-chip { background: rgba(250, 248, 243, .93); color: var(--muted);
  box-shadow: inset 0 0 0 1px var(--line-2); }

/* The line under the title. It carries the state in words and it carries the
   date, because "not listed" without a when is a fact with no size. */
.tc-off-line { display: block; margin-top: 6px;
  font-family: var(--sans); font-size: 9.5px; font-weight: 600;
  letter-spacing: .14em; text-transform: uppercase; color: var(--muted); }
/* rp133: the distance chip beside the trade's flag, when the search named a place */
.tc-chip + .tc-chip-dist { margin-left: 6px; }
