/* noticeboard.css — academic pinned-notice treatment for card sections.

   SCOPE: opt in per section by adding class="nb-board" to the <section>.
   Everything below is scoped under .nb-board, so a page only gets the board
   where it explicitly asks for it. Nothing leaks to other sections or pages
   (see: the unscoped `.cell !important` incident, 2026-09-21).

   Deliberately NOT keyed to body[data-page="..."]: /s6-comp and /s7-compiler
   ship a bare <body> with no data-page attribute, so that hook cannot reach
   them.

   Palette uses existing site tokens only: --paper-2, --wash, --line,
   --accent-ink.
*/

.nb-board {
  --nb-board: #e7dfcc;
  --nb-frame: #cdbfa3;
  --nb-pin-a: #9c3b22;
  --nb-pin-b: #4a6670;
  --nb-pin-c: #8a7430;
  --nb-pin-d: #55694a;

  position: relative;
  margin: 48px 0 32px;
  padding: 28px 26px 30px;
  border: 1px solid var(--nb-frame);
  border-radius: 6px;
  background-color: var(--nb-board);
  /* Paper grain: two low-contrast dot fields, no image weight. */
  background-image:
    radial-gradient(circle at 12% 18%, rgba(120, 98, 62, .055) 0 1.1px, transparent 1.4px),
    radial-gradient(circle at 63% 71%, rgba(120, 98, 62, .045) 0 1px, transparent 1.3px);
  background-size: 17px 17px, 23px 23px;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .5),
              inset 0 0 44px rgba(120, 98, 62, .11);
}

/* Inner rule line — reads as a board edge without a second border. */
.nb-board::before {
  content: "";
  position: absolute;
  inset: 7px;
  border: 1px solid rgba(160, 140, 104, .3);
  border-radius: 3px;
  pointer-events: none;
}

.nb-board .sect {
  position: relative;
  margin: 0 0 22px;
}

/* Section sits under a sticky nav; match the site's 80px convention. */
.nb-board {
  scroll-margin-top: 80px;
}

/* ---- Board surface ----
   Masonry via columns so notices of different heights pack tightly with no
   dead gap (a 2-col grid leaves a ragged hole under the short cards).
   Rotation stays OFF column children — transforms on a column child clip in
   Safari; the rattle is applied to an inner wrapper instead. */
.nb-board .cards {
  display: block;
  column-count: 1;
  column-gap: 20px;
  gap: 0;
  grid-template-columns: none;
  position: relative;
}

@media (min-width: 720px) {
  .nb-board .cards {
    column-count: 2;
  }
}

/* ---- Notices ---- */
.nb-board .cards > .card,
.nb-board .cards > .featured-card {
  width: 100%;
  margin: 0 0 26px;
  break-inside: avoid;
  -webkit-column-break-inside: avoid;
  display: inline-block;
  vertical-align: top;

  position: relative;
  padding: 26px 22px 20px;
  border: 1px solid rgba(150, 130, 96, .42) !important;
  border-radius: 2px;
  background: var(--nb-paper, #fbf7ec);
  overflow: visible;
  box-shadow: 0 1px 1px rgba(60, 48, 28, .10),
              0 7px 16px -10px rgba(60, 48, 28, .40);
  transition: box-shadow .22s ease, transform .22s ease;
}

/* Kill the inherited red top-bar; the notice is the object now. */
.nb-board .cards > .featured-card::before {
  content: none !important;
}

/* Paper stock.

   Default: ONE tone per board. Within a section the notices are homogeneous
   (six `CST463 · WEB PROG` answer keys; six `EXP n` lab sheets), so cycling
   four colours across them encodes nothing — it reads as stripes. One tone
   per board makes the colour mean "this section", which is true.

   Opt in to per-card variation with class="nb-varied" on the section, for
   boards whose notices are genuinely different in kind (the homepage
   Featured strip: an award, a talk, a tool, a contributions log). */
.nb-board .cards > .card { --nb-paper: #fbf7ec; }

.nb-board.nb-stock-b .cards > .card { --nb-paper: #f6eed6; }
.nb-board.nb-stock-c .cards > .card { --nb-paper: #e9eef0; }
.nb-board.nb-stock-d .cards > .card { --nb-paper: #e9eee4; }

.nb-board.nb-varied .cards > .card:nth-child(4n+1) { --nb-paper: #fbf7ec; }
.nb-board.nb-varied .cards > .card:nth-child(4n+2) { --nb-paper: #f6eed6; }
.nb-board.nb-varied .cards > .card:nth-child(4n+3) { --nb-paper: #e9eef0; }
.nb-board.nb-varied .cards > .card:nth-child(4n+4) { --nb-paper: #e9eee4; }

/* ---- Pushpin ---- */
.nb-board .cards > .card::after {
  content: "";
  position: absolute;
  top: -7px;
  left: 50%;
  width: 13px;
  height: 13px;
  margin-left: -6.5px;
  border-radius: 50%;
  background:
    radial-gradient(circle at 34% 30%, rgba(255, 255, 255, .85) 0 2px, transparent 2.6px),
    var(--nb-pin, var(--nb-pin-a));
  box-shadow: 0 1.5px 3px rgba(40, 30, 16, .45);
  z-index: 3;
}

/* Pin colour follows the same rule as paper: one per board by default, cycled
   only where the notices genuinely differ (.nb-varied). Pin POSITION still
   alternates everywhere — that irregularity is what sells a hand-pinned
   board, and unlike colour it encodes nothing, so it cannot mislead. */
.nb-board .cards > .card { --nb-pin: var(--nb-pin-a); }
.nb-board.nb-stock-b .cards > .card { --nb-pin: var(--nb-pin-c); }
.nb-board.nb-stock-c .cards > .card { --nb-pin: var(--nb-pin-b); }
.nb-board.nb-stock-d .cards > .card { --nb-pin: var(--nb-pin-d); }

.nb-board.nb-varied .cards > .card:nth-child(4n+1) { --nb-pin: var(--nb-pin-a); }
.nb-board.nb-varied .cards > .card:nth-child(4n+2) { --nb-pin: var(--nb-pin-c); }
.nb-board.nb-varied .cards > .card:nth-child(4n+3) { --nb-pin: var(--nb-pin-b); }
.nb-board.nb-varied .cards > .card:nth-child(4n+4) { --nb-pin: var(--nb-pin-d); }

.nb-board .cards > .card:nth-child(odd)::after  { left: 30%; }
.nb-board .cards > .card:nth-child(even)::after { left: 68%; }

/* ---- Typographic hierarchy: label / heading / body ---- */
.nb-board .cards > .card .code,
.nb-board .cards > .card .badge {
  color: var(--accent-ink) !important;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .13em;
  text-transform: uppercase;
  margin-bottom: 10px;
  padding-bottom: 8px;
  border-bottom: 1px dashed rgba(150, 130, 96, .45);
}

.nb-board .cards > .card .code .live {
  background: rgba(255, 255, 255, .72);
  border: 1px solid rgba(150, 130, 96, .4);
}

.nb-board .cards > .card .name,
.nb-board .cards > .card h3 {
  font-family: var(--serif);
  font-size: 27px;
  line-height: 1.08;
  letter-spacing: -.02em;
  margin: 0 0 11px;
}

.nb-board .cards > .card .meta {
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--ink-2);
}

.nb-board .cards > .card .go {
  padding-top: 10px;
  border-top: 1px dashed rgba(150, 130, 96, .4);
  /* .go is a flex row with justify-content spreading its children; when the
     label wraps on narrow screens the arrow is stranded at the far right.
     Pack to the start and let it sit against the last word instead. */
  display: flex !important;
  justify-content: flex-start !important;
  flex-wrap: wrap;
  gap: 0 6px;
}

.nb-board .featured-figure {
  border-color: rgba(150, 130, 96, .4);
  border-radius: 2px;
}

/* ---- Hover: lift + gentle rattle ---- */
.nb-board .cards > .card:hover {
  box-shadow: 0 2px 3px rgba(60, 48, 28, .13),
              0 14px 26px -12px rgba(60, 48, 28, .5);
  border-color: rgba(150, 130, 96, .7) !important;
}

/* Rattle + settle run on an inner wrapper (.nb-sheet, added by JS). Rotating
   the column child itself clips in Safari's multi-column layout. */
.nb-board .cards > .card .nb-sheet {
  display: block;
  transform-origin: 50% -22px;
}
.nb-board .cards > .card:nth-child(odd)  .nb-sheet { transform-origin: 30% -22px; }
.nb-board .cards > .card:nth-child(even) .nb-sheet { transform-origin: 68% -22px; }

.nb-board .cards > .card:hover .nb-sheet {
  animation: nb-rattle .5s ease-in-out;
}

/* Pivot from the pin, not the centre — that is what sells it as pinned. */
@keyframes nb-rattle {
  0%   { transform: rotate(0deg); }
  22%  { transform: rotate(.5deg); }
  48%  { transform: rotate(-.38deg); }
  74%  { transform: rotate(.2deg); }
  100% { transform: rotate(0deg); }
}

/* ---- Scroll-in: settle onto the board ---- */
.nb-board .cards > .card.nb-pending .nb-sheet {
  opacity: 0;
  transform: translateY(-9px) rotate(-1.2deg);
}

.nb-board .cards > .card.nb-in .nb-sheet {
  opacity: 1;
  transform: translateY(0) rotate(0deg);
  transition: opacity .45s ease, transform .55s cubic-bezier(.2, .9, .3, 1.08);
}

/* The markup already ends some links with a literal arrow; .go::after adds a
   second one. Suppress the pseudo-arrow on those. */
.nb-board .cards > .card .go.nb-has-arrow::after {
  content: none;
}

/* ---- Reduced motion: no animation, nothing hidden ---- */
@media (prefers-reduced-motion: reduce) {
  .nb-board .cards > .card,
  .nb-board .cards > .card .nb-sheet,
  .nb-board .cards > .card.nb-pending .nb-sheet,
  .nb-board .cards > .card.nb-in .nb-sheet {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
    animation: none !important;
  }
  .nb-board .cards > .card:hover,
  .nb-board .cards > .card:hover .nb-sheet {
    animation: none !important;
  }
}
