/* ============================================================
   THE SHIELD — sitewide save / follow control
   Red Warden Studios · v1 · 2026-08-03

   Outline = not shielded.  Filled (split white/crimson) = shielded.
   The filled state IS the real Red Warden mark. The nav logo is that
   same shield WITH the warden figure — that is the difference, and it
   is deliberate. Never reuse assets/logo-icon.svg as this control.

   HARD RULE: this component NEVER renders a count. No "42 people
   shielded this". Shield shows the current member's own state only.
   ============================================================ */

.rw-shield {
  /* --rw-shield-size is the mark's HEIGHT. The shield is 0.6125 as wide
     as it is tall, so sizing by width would make it read tiny next to
     body text. 24px height ≈ 14.7px wide. */
  --rw-shield-size: 24px;
  --rw-shield-rest: rgba(245, 245, 245, 0.55);
  --rw-shield-hover: var(--rws-crimson, #C0392B);
  --rw-shield-crimson: var(--rws-crimson, #C0392B);
  --rw-shield-pale: var(--rws-white, #F5F5F5);
  /* band = definition on light backgrounds, rim = definition on dark.
     Both stay constant across divisions; only the faces change colour. */
  --rw-shield-band: #0A0A0B;
  --rw-shield-rim: #C6C5C4;

  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  padding: 6px 8px;
  margin: -6px -8px;              /* keeps the 44px tap target off the layout */
  min-width: 44px;
  min-height: 44px;
  justify-content: center;

  background: none;
  border: 0;
  border-radius: 6px;
  color: var(--rw-shield-rest);
  font: inherit;
  font-family: var(--font-body, 'Inter', system-ui, sans-serif);
  font-size: 0.88rem;
  letter-spacing: 0.04em;
  line-height: 1;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: color 0.2s ease;
}

.rw-shield:focus-visible {
  outline: 2px solid var(--rws-crimson, #C0392B);
  outline-offset: 2px;
}

/* ---------- the mark ---------- */

.rw-shield__svg {
  height: var(--rw-shield-size);
  width: auto;
  flex: 0 0 auto;
  overflow: visible;
  /* optical centre of a shield sits below its geometric centre */
  transform-origin: 50% 56%;
  transition: transform 0.18s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.rw-shield__body,
.rw-shield__half {
  fill: transparent;
  transition: fill 0.22s ease;
}

/* Dark band + light rim. Progressively narrower strokes of the same path,
   clipped to it, so each later one lands nearer the edge.
   The band carries the mark on light and mid backgrounds; the rim carries
   it on dark ones. Together they survive any background — including the
   colourful ones we haven't designed yet. Verified across six. */
.rw-shield__band,
.rw-shield__rim {
  fill: none;
  stroke: transparent;
  stroke-linejoin: round;
  transition: stroke 0.22s ease;
}

.rw-shield__band { stroke-width: 5.2; }
.rw-shield__rim  { stroke-width: 2.6; }

/* Stroke width is in viewBox units so it scales with the mark — a
   non-scaling stroke would be too heavy at 16px and too thin at 48px.
   2.4 units ≈ 1.8px at the 24px default. */
.rw-shield__edge {
  fill: none;
  stroke: currentColor;
  stroke-width: 2.4;
  stroke-linejoin: round;
  stroke-linecap: round;
  transition: stroke 0.2s ease, stroke-width 0.2s ease;
}

/* ---------- hover / press ---------- */

@media (hover: hover) {
  .rw-shield:hover {
    color: var(--rw-shield-hover);
  }
  .rw-shield:hover .rw-shield__svg {
    transform: scale(1.1);
  }
}

.rw-shield:active .rw-shield__svg {
  transform: scale(0.92);
}

/* ---------- shielded ---------- */

.rw-shield.is-shielded {
  color: var(--rws-white, #F5F5F5);
}

.rw-shield.is-shielded .rw-shield__body {
  fill: var(--rw-shield-crimson);
}

.rw-shield.is-shielded .rw-shield__half {
  fill: var(--rw-shield-pale);
}

.rw-shield.is-shielded .rw-shield__band { stroke: var(--rw-shield-band); }
.rw-shield.is-shielded .rw-shield__rim  { stroke: var(--rw-shield-rim); }

/* The outline is the unshielded state only — once filled, the band and
   rim define the edge and a third stroke on top just muddies it. */
.rw-shield.is-shielded .rw-shield__edge {
  stroke: transparent;
}

/* the commit pop — fires once, on the transition into shielded */
.rw-shield.is-committing .rw-shield__svg {
  animation: rw-shield-pop 0.42s cubic-bezier(0.34, 1.56, 0.64, 1);
}

@keyframes rw-shield-pop {
  0%   { transform: scale(0.8); }
  45%  { transform: scale(1.22); }
  100% { transform: scale(1); }
}

/* ---------- pairing with a primary link ---------- */

/* Sits a shield beside the page's own call-to-action without either one
   dictating the other's spacing. The shield's negative margins (see above)
   already keep its 44px tap target out of the layout, so the gap here is the
   real visual gap. Wraps on narrow screens rather than crushing the link. */
.rw-shield-row {
  display: flex;
  align-items: center;
  gap: 22px;
  flex-wrap: wrap;
}

/* ---------- nav account control ----------
   The same silhouette as the Shield, at nav scale, so the mark in the nav,
   the mark you fill by saving, and the Visitor's seat at 6:00 all read as
   one thing.

   Drawn as a MASK from a data-URI rather than inline SVG: the markup then
   costs one <span> per page instead of a 300-character path repeated across
   13 pages x 2 navs. `background: currentColor` means it inherits whatever
   colour the surrounding nav link already uses — including :hover and
   .active — on pages whose nav CSS we do not otherwise touch.

   If mask-image is unsupported the span simply renders nothing and the word
   "Account" still reads. No layout shift, no broken glyph.               */

.nav-account {
  display: inline-flex;
  align-items: center;
  gap: 0.55em;
}

.nav-account-mark {
  flex: 0 0 auto;
  width: 0.78em;               /* the shield is 0.6125 as wide as it is tall */
  height: 1.27em;
  background: currentColor;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-position: center; mask-position: center;
  -webkit-mask-size: contain;   mask-size: contain;
  -webkit-mask-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 29 46'><path d='M14.5 1 L1 8.99 C1 11.75 2.42 19.31 3.85 24.33 C5.01 28.39 6.8 32.25 8.72 36.07 C10.64 39.87 11.9 41.4 14.5 45 C17.1 41.4 18.36 39.87 20.28 36.07 C22.2 32.25 23.99 28.39 25.15 24.33 C26.58 19.31 28 11.75 28 8.99 Z' fill='none' stroke='%23000' stroke-width='2.6' stroke-linejoin='round' stroke-linecap='round'/></svg>");
          mask-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 29 46'><path d='M14.5 1 L1 8.99 C1 11.75 2.42 19.31 3.85 24.33 C5.01 28.39 6.8 32.25 8.72 36.07 C10.64 39.87 11.9 41.4 14.5 45 C17.1 41.4 18.36 39.87 20.28 36.07 C22.2 32.25 23.99 28.39 25.15 24.33 C26.58 19.31 28 11.75 28 8.99 Z' fill='none' stroke='%23000' stroke-width='2.6' stroke-linejoin='round' stroke-linecap='round'/></svg>");
}

/* ---------- signed in (K8) ----------
   Same shield, filled instead of outlined — the exact grammar the Shield
   control already uses everywhere else on the site, so it needs no explaining.
   The WORD stays "Account": a nav item that renames itself when your state
   changes is a nav item people stop being able to find.

   Applied by js/rws-session.js from a server answer, never from a stored flag.
   Deliberately no width or spacing change, so the control cannot shift when the
   answer lands a moment after first paint. */
.nav-account.is-member .nav-account-mark {
  color: var(--rws-crimson, #C0392B);
  background: currentColor;
  -webkit-mask-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 29 46'><path d='M14.5 1 L1 8.99 C1 11.75 2.42 19.31 3.85 24.33 C5.01 28.39 6.8 32.25 8.72 36.07 C10.64 39.87 11.9 41.4 14.5 45 C17.1 41.4 18.36 39.87 20.28 36.07 C22.2 32.25 23.99 28.39 25.15 24.33 C26.58 19.31 28 11.75 28 8.99 Z' fill='%23000'/></svg>");
          mask-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 29 46'><path d='M14.5 1 L1 8.99 C1 11.75 2.42 19.31 3.85 24.33 C5.01 28.39 6.8 32.25 8.72 36.07 C10.64 39.87 11.9 41.4 14.5 45 C17.1 41.4 18.36 39.87 20.28 36.07 C22.2 32.25 23.99 28.39 25.15 24.33 C26.58 19.31 28 11.75 28 8.99 Z' fill='%23000'/></svg>");
}

/* Desktop nav: set the account link slightly apart from the section links —
   it is a different KIND of destination, not another part of the site. */
.nav-links .nav-account {
  margin-left: 0.4rem;
  padding-left: 1.15rem;
  border-left: 1px solid rgba(255, 255, 255, 0.14);
}

/* ---------- label ---------- */

.rw-shield__label {
  white-space: nowrap;
  text-transform: uppercase;
  font-weight: 500;
}

.rw-shield--icon-only {
  gap: 0;
}

.rw-shield--icon-only .rw-shield__label {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Icon-only ONLY WHERE THE LABEL WILL NOT FIT. Added 2026-08-16 for the
   Commander card in the account, where "FOLLOWING" makes the control ~130px
   wide — at 390px that left the text about 150px and the button printed over
   the top of "RedWardenCDR".

   🚨 IT LIVES HERE, NOT IN account.css, ON PURPOSE. The obvious fix was to
   repeat the six visually-hidden properties inside account.css's media query.
   That would be a SECOND COPY of how this component hides its own label, free
   to drift from the first — the same defect as the three hardcoded ROSTER
   colours in LLypses, where @INTEL was #00ffee in one copy and #a78bfa in the
   others. The component owns its label; anything wanting the label hidden asks
   for it by class.

   ⚠️ 640px matches account.css's only breakpoint. A component-level media query
   with its own number would be a fifth disagreeing breakpoint — the NAV-OVERFLOW
   lesson. If that number ever moves, it moves in both files together.
   ⚠️ Still ANNOUNCED, never removed: the label is clipped, not display:none, and
   shield.js keeps aria-label in step. A bare mark with no accessible name is a
   button that says nothing. */
@media (max-width: 640px) {
  .rw-shield--icon-only-narrow { gap: 0; }
  .rw-shield--icon-only-narrow .rw-shield__label {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
  }
}

/* ---------- division marks ----------
   Andrew, 2026-08-15, looking at a crimson shield on the sapphire Bastion page:
   "shield colors should match the themes of their divisions."

   🚨 BASTION HAD NO RULE AT ALL. Athenaeum, MythrilWorks and Foundry each had
   one; Bastion was simply never written, so it fell through to the base
   --rw-shield-crimson (#C0392B) and sat plain crimson on a sapphire page.
   A missing variant is invisible in review precisely because it still renders
   something plausible. ⚠️ There are FOUR houses — check for four rules.

   🚨 THE THREE THAT EXISTED WERE STALE. They were desaturated hexes written
   before the 2026-08-14 palette overhaul (workshop brief adopted into
   NAV_AND_IDENTITY_SPEC §3a), so none of them matched their division any more:
       foundry        #B4442A  vs house accent #FF6A00
       mythrilworks   #2E6DA8  vs house accent #22E0FF
       athenaeum      #A6752E  vs house accent #E8B95C
   Hard-coding a fourth hex here would have made it stale on the next refresh
   too, so these now READ THE HOUSE TOKENS instead of copying them.

   ⭐ WHY `--house-accent-<name>` AND NOT `--house-accent`. The unsuffixed token
   is declared on `body.house-x`, so it resolves to whichever house the BODY is
   wearing — on the account page that repainted every division's badge in the
   active house's colour. houses.css publishes the four suffixed tokens at :root
   exactly for this case. Literal fallbacks are kept so a page that loads
   shield.css WITHOUT houses.css still gets the right mark rather than nothing.

   The pale half stays a near-white tinted toward its accent: the mark is a
   two-tone split, and tinting both halves would lose the split.              */

.rw-shield--bastion {
  --rw-shield-crimson: var(--house-accent-bastion, #4F7BE8);
  --rw-shield-pale: #DEE6FA;
  --rw-shield-hover: #7FA0F0;
}

.rw-shield--foundry {
  --rw-shield-crimson: var(--house-accent-foundry, #FF6A00);
  --rw-shield-pale: #F2E2D4;
  --rw-shield-hover: #FF8C3A;
}

.rw-shield--mythrilworks {
  --rw-shield-crimson: var(--house-accent-mythrilworks, #22E0FF);
  --rw-shield-pale: #DCF4FB;
  --rw-shield-hover: #6BEBFF;
}

/* The Athenaeum keeps its own gesture — Andrew's earlier call that here the
   shield reads as "Read later" rather than "Save" — but now in the house's
   own gilt rather than a hex that drifted away from it. */
.rw-shield--athenaeum {
  --rw-shield-crimson: var(--house-accent-athenaeum, #E8B95C);
  --rw-shield-pale: #F6EDDA;
  --rw-shield-hover: #F2D08A;
}

/* ── THE COMMANDER ─────────────────────────────────────────────────────────
   Added 2026-08-16. RedWardenCDR is the only shield that wears the ORIGINAL
   crimson rather than a division colour — Andrew's call: the four houses each
   took their own, the Commander keeps the mark the company was built on.

   So the colour alone cannot distinguish it: at 24px an unlabelled crimson
   shield is the parent shield, which is exactly what it should look like AND
   exactly why it needs one more signal. That signal is a CHROME RING.

   ⭐ THE RING IS THE SANCTIONED ANSWER, NOT A NEW IDEA.
   CHAMBER_AND_EMBLEMS.md closed the CDR emblem on 2026-08-01 with the crown,
   coronet and smoothed battlements all rejected — "the steps are the character
   of the mark" — and left exactly one door open:
     "If the 'shows command, not just protection' question ever needs
      answering, solve it with CONTEXT rather than shape — placement at the head
      of the table, AN ENCIRCLING RING, a colour treatment, or the character
      art. NEVER by re-cutting the silhouette."
   Andrew proposed the ring independently on 2026-08-16. It touches no path in
   the SVG, so the emblem stays closed. ⚠️ Do not "improve" this into a shape
   change — that is the one thing the spec forbids.

   Implementation notes: the ring is an outline on the BUTTON, not a stroke on
   the mark, so it cannot disturb the shield geometry the rest of this file
   depends on. It is drawn with box-shadow rather than border because border
   would shift the 44px tap target and re-flow every layout the shield sits in. */
.rw-shield--cdr {
  --rw-shield-crimson: var(--house-accent-cdr, #C0392B);
  --rw-shield-pale: #F5E9E7;
  --rw-shield-hover: #E0574A;
  /* brushed chrome, not white — a pure white ring reads as a focus state */
  --rw-shield-ring: rgba(214, 218, 222, 0.42);
  --rw-shield-ring-lit: rgba(232, 236, 240, 0.85);
  border-radius: 999px;
  box-shadow: 0 0 0 1px var(--rw-shield-ring);
  transition: box-shadow .25s ease;
}
.rw-shield--cdr:hover,
.rw-shield--cdr:focus-visible {
  box-shadow: 0 0 0 1px var(--rw-shield-ring-lit),
              0 0 14px rgba(192, 57, 43, 0.30);
}
/* Shielded state: the ring warms toward the accent so the ring and the fill
   agree, instead of a cold outline sitting on a crimson mark. */
.rw-shield--cdr[aria-pressed="true"] {
  box-shadow: 0 0 0 1px rgba(224, 87, 74, 0.55),
              0 0 16px rgba(192, 57, 43, 0.22);
}
@media (prefers-reduced-motion: reduce) {
  .rw-shield--cdr { transition: none; }
}

/* ---------- follow badge ----------
   Only appears on ONGOING objects (games, series, unreleased software,
   divisions) once shielded — finished things never show it, because
   finished things have nothing to announce.                          */

/* The .rw-shield__follow "notify me" pill was REMOVED 2026-08-07. It lived
   inside the button, so clicking it toggled the shield off — it looked like a
   second control and acted as an undo. "Following" plus a filled shield already
   says you are being notified; turning notifications down belongs in account
   settings. js/shield.js no longer creates the element, so these rules are gone
   rather than left to rot. Do not re-add them. */

/* ---------- over imagery ----------
   The band+rim pair handles flat backgrounds. Over a photo, a video or a
   busy gradient, the UNSHIELDED outline can still get lost because it is a
   single hairline. Add this modifier there. Costs one filter, no markup. */

.rw-shield--on-image .rw-shield__svg {
  filter: drop-shadow(0 1px 1.5px rgba(0, 0, 0, 0.75));
}

/* ---------- motion preferences ---------- */

@media (prefers-reduced-motion: reduce) {
  .rw-shield__svg,
  .rw-shield__body,
  .rw-shield__half,
  .rw-shield__edge {
    transition: none;
  }
  .rw-shield.is-committing .rw-shield__svg {
    animation: none;
  }
}

/* ---------- placement helper ----------
   For a shield pinned to the corner of a card or tile.              */

.rw-shield--corner {
  position: absolute;
  top: 10px;
  right: 10px;
  z-index: 3;
  background: rgba(8, 8, 9, 0.55);
  backdrop-filter: blur(4px);
  border-radius: 8px;
  padding: 8px;
  margin: 0;
}
