/* ─────────────────────────────────────────────────────────────────────────
   PUSSY FACTORY, INC. — the EMPLOYEE ID BADGE component (the lineup card).

   This is the reusable extraction of the badge design that debuted on the
   /orientation/ lineup. It is the house way of presenting a person — a DJ, a
   performer, a host — and it is meant to be reused: on landing pages, on the
   builder's event pages, anywhere a lineup is shown.

   USAGE
     <link rel="stylesheet" href="/styles.css" />     <- brand tokens first
     <link rel="stylesheet" href="/pf-badge.css" />

     <!-- once per page: the PF bug, referenced by every badge -->
     <svg width="0" height="0" style="position:absolute" aria-hidden="true">
       <symbol id="pfbug" viewBox="0 0 100 100">…</symbol></svg>

     <article class="act">
       <span class="notch" aria-hidden="true"></span>
       <p class="bhdr"><svg class="bug" aria-hidden="true"><use href="#pfbug"/></svg>
          <span class="org">Pussy Factory</span><span class="eid">Employee ID</span></p>
       <div class="mug"><img src="…" alt="" loading="lazy" /></div>
       <div class="bbody">
         <span class="when">10 PM – 11 PM</span>          <- optional set time
         <h4 class="who">Knoll</h4>
         <a class="ig" href="…">@djknoll.303</a>          <- optional
         <div class="brow"><span class="idno">No. K4Q&#8209;7X2</span>
              <span class="bc" aria-hidden="true"></span></div>
       </div>
     </article>

   The badge is LANDSCAPE by default: the photo is a 1:1 square that fills the
   card's height, so give the card a height (or let a grid stretch it) and the
   photo column sizes itself. `.act.portrait` flips it to photo-on-top.

   VARIANTS
     .act.portrait — photo above the text (for a headline act / a tall slot)
     .act.vacant   — an unfilled position: silhouette, redacted name, no photo.
                     Used to tease a lineup that hasn't been announced.

   CLASS NAMES ARE DELIBERATELY THE SAME as the orientation page's scoped copy
   (.act/.bhdr/.mug/.bbody/.who/.when/.ig/.brow/.idno/.bc/.notch) so markup is
   copy-paste compatible between the two. Orientation still carries its own
   scoped copy plus its schedule/time-axis layout; only the badge itself lives
   here. Layout AROUND the badges (grids, time axes, flow connectors) is the
   page's job — this file styles one card and nothing else.

   Depends on the brand tokens in styles.css: --paper-2/-3, --ink, --ink-soft,
   --line-2, --taupe, --burgundy.
   ───────────────────────────────────────────────────────────────────────── */

/* ---------- the card ---------- */
.act {
  position: relative; display: grid; overflow: hidden;
  border: 1px solid var(--ink); border-radius: 3px;
  /* photo column sizes to the square (see .mug); text column takes the rest */
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-rows: auto minmax(0, 1fr);
  background: linear-gradient(180deg, color-mix(in srgb, var(--paper-2) 95%, #fff), var(--paper-3));
  box-shadow: 0 16px 36px -34px rgba(34, 29, 24, .9);
}
/* the burgundy spine down the left edge */
.act::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 4px;
  background: var(--burgundy); z-index: 3; }

/* the lanyard slot, punched through the header strip */
.notch { position: absolute; top: 3px; left: 50%; transform: translateX(-50%);
  width: 26px; height: 5px; z-index: 4; border-radius: 3px;
  background: color-mix(in srgb, var(--ink) 32%, transparent); }

/* ---------- header strip: PF bug + org + EMPLOYEE ID ---------- */
.bhdr { grid-column: 1 / -1; grid-row: 1; display: flex; align-items: center; gap: 7px;
  background: var(--ink); color: var(--paper); padding: 5px 9px 5px 12px;
  font-family: "Space Mono", monospace; font-size: 8px; letter-spacing: .16em; text-transform: uppercase; }
.bhdr .bug { width: 13px; height: 13px; border-radius: 2px; flex: none; display: block; }
.bhdr .org { font-family: "Black Ops One", system-ui, sans-serif; letter-spacing: .03em; font-size: 9.5px; }
.bhdr .eid { margin-left: auto; color: color-mix(in srgb, var(--paper) 60%, var(--burgundy)); }

/* ---------- the ID photo: always a 1:1 square ----------
   On the landscape card it fills the card's HEIGHT and its width follows (auto
   column), so it autosizes with the card and never overflows the header. */
.mug { grid-column: 1; grid-row: 2; position: relative; overflow: hidden;
  border-right: 1px solid var(--line-2);
  aspect-ratio: 1 / 1; height: 100%; width: auto; justify-self: start; align-self: stretch;
  background: radial-gradient(120% 90% at 50% 12%,
    color-mix(in srgb, var(--paper-2) 70%, #fff) 0%, var(--paper-3) 60%,
    color-mix(in srgb, var(--paper-3) 82%, #000) 100%); }
.mug img { width: 100%; height: 100%; object-fit: cover; filter: grayscale(1) contrast(1.06); }

/* ---------- the text fields ----------
   min-width:0 + overflow:hidden let the fields clip cleanly on narrow phones
   instead of spilling past the card. */
.bbody { grid-column: 2; grid-row: 2; display: flex; flex-direction: column; justify-content: center;
  gap: 5px; min-width: 0; overflow: hidden;
  padding: clamp(8px, 3vw, 13px) clamp(10px, 3.5vw, 15px); }

/* the name. line-height 1 + overflow:visible so tall Black Ops One caps are not
   cropped at the box bottom; .bbody clips long names horizontally instead. Pages
   with long names should auto-fit the size in JS so nothing wraps. */
.who { font-family: "Black Ops One", system-ui, sans-serif; font-weight: 400; text-transform: uppercase;
  letter-spacing: .012em; line-height: 1; color: var(--ink); white-space: nowrap; margin: 0;
  max-width: 100%; overflow: visible; font-size: clamp(20px, 6.2vw, 30px); }

.when { font-family: "Space Mono", monospace; font-size: 9px; letter-spacing: .14em; text-transform: uppercase;
  color: var(--burgundy); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }

.ig { align-self: flex-start; display: inline-block; max-width: 100%;
  font-family: "Space Mono", monospace; font-size: 8.5px; letter-spacing: .06em; color: var(--burgundy);
  text-decoration: none; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  border-bottom: 1px solid color-mix(in srgb, var(--burgundy) 40%, transparent);
  transition: color .18s, border-color .18s; }
.ig:hover, .ig:focus-visible { color: var(--ink); border-bottom-color: var(--ink); outline: none; }

.brow { display: flex; align-items: center; gap: 9px; margin-top: 2px; min-width: 0;
  font-family: "Space Mono", monospace; font-size: 8px; letter-spacing: .1em; text-transform: uppercase;
  color: var(--taupe); }
.brow .idno { color: var(--ink); font-weight: 700; letter-spacing: .06em;
  min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bc { width: 50px; height: 13px; opacity: .5; flex: none; margin-left: auto;
  background: repeating-linear-gradient(90deg, var(--ink) 0 1px, transparent 1px 2px, var(--ink) 2px 4px,
    transparent 4px 5px, var(--ink) 5px 7px, transparent 7px 9px, var(--ink) 9px 10px, transparent 10px 12px); }

/* ---------- variant: portrait (photo on top) ---------- */
.act.portrait { grid-template-columns: 1fr; grid-template-rows: auto auto 1fr; }
.act.portrait .mug { grid-column: 1; grid-row: 2; width: 100%; height: auto;
  justify-self: stretch; align-self: start; border-right: 0; border-bottom: 1px solid var(--line-2); }
.act.portrait .bbody { grid-column: 1; grid-row: 3; gap: 3px; }

/* ---------- variant: vacant (position not yet filled) ----------
   No photo on file: a plate-grey silhouette behind a diagonal "no photo" slug,
   and the name/number struck out as redaction bars. */
.act.vacant { border-style: dashed; border-color: color-mix(in srgb, var(--ink) 55%, transparent);
  box-shadow: none; background: color-mix(in srgb, var(--paper-2) 55%, transparent); }
.act.vacant::before { background: color-mix(in srgb, var(--burgundy) 45%, transparent); }
.act.vacant .bhdr { background: color-mix(in srgb, var(--ink) 72%, var(--paper)); }
.act.vacant .mug { border-right-color: color-mix(in srgb, var(--ink) 25%, transparent); }
/* the silhouette: a head-and-shoulders sitter, drawn with two radial stops.
   No "no photo on file" slug over it — the header strip already says VACANT, and
   a second label on a 100px square just muddies the silhouette. */
.act.vacant .mug { background:
  radial-gradient(circle at 50% 88%, color-mix(in srgb, var(--taupe) 55%, transparent) 0 34%, transparent 34.5%),
  radial-gradient(circle at 50% 38%, color-mix(in srgb, var(--taupe) 55%, transparent) 0 17%, transparent 17.5%),
  linear-gradient(180deg, color-mix(in srgb, var(--paper-3) 70%, #fff), var(--paper-3)); }
.act.vacant .who { font-size: clamp(15px, 3.4vw, 19px); }

/* ---------- variant: redacted (a real badge, name withheld) ----------
   A filled position whose holder is not named publicly. The badge is intact —
   solid border, full chrome — and only the NAME is struck. Use this for guest
   or staff rosters: it publishes the department, the title and the number
   without publishing a person. */
.act.redacted .who { font-size: clamp(15px, 3.2vw, 20px); }
.act.redacted .mug { background:
  radial-gradient(circle at 50% 88%, color-mix(in srgb, var(--taupe) 62%, transparent) 0 34%, transparent 34.5%),
  radial-gradient(circle at 50% 38%, color-mix(in srgb, var(--taupe) 62%, transparent) 0 17%, transparent 17.5%),
  linear-gradient(180deg, color-mix(in srgb, var(--paper-3) 74%, #fff), var(--paper-3)); }
/* a department line above the name */
.dept { font-family: "Space Mono", monospace; font-size: 8px; letter-spacing: .2em; text-transform: uppercase;
  color: var(--taupe); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }

/* the job title, set in an ink bar exactly as it prints on the physical badge
   (see the station's badge design: name in serif, title reversed out below it) */
.jobtitle { align-self: stretch; background: var(--ink); color: var(--paper);
  padding: 3px 7px; margin-top: 1px;
  font-family: "Space Mono", monospace; font-weight: 700; font-size: 8.5px;
  letter-spacing: .1em; text-transform: uppercase; line-height: 1.35;
  overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; }
.act.vacant .jobtitle { background: color-mix(in srgb, var(--ink) 62%, var(--paper)); }

/* redaction bars stand in for the fields that are withheld or not yet filled.
   Give the bar its text so the bar sizes itself and screen readers still get
   "withheld" rather than a run of block characters. */
.pfb-redact { display: inline-block; background: var(--ink); color: transparent;
  border-radius: 1px; user-select: none; vertical-align: baseline;
  box-decoration-break: clone; -webkit-box-decoration-break: clone; }

@media (prefers-reduced-motion: reduce) {
  .act, .ig { transition: none; }
}
