/* ==========================================================================
   neobrutalism-theme / play layer
   --------------------------------------------------------------------------
   The dimensional half of the register: tilt, stickers that hang off their
   container, extruded motifs, hand drawn marks, and a taped section seam.

   This file and assets/ are separable from the rest of the theme. Delete both
   and everything still works, quietly. That deletion is a real move along the
   framework's axes and CUSTOMIZE.md names it.

   Every value here is a var() reference to tokens.css. There is not one color
   literal in this file.

   Requires tokens.css. Load it, then components.css, then this:

     <link rel="stylesheet" href="tokens/tokens.css" />
     <link rel="stylesheet" href="components/components.css" />
     <link rel="stylesheet" href="components/play.css" />

   The SVGs in assets/ are meant to be inlined into your markup rather than
   loaded through <img>. An SVG in an <img> is an isolated document that cannot
   read the page's custom properties, so it would have to carry its own color
   values and the single source of truth would be gone. Inlined, the paint
   rules below reach them. Opened on their own the files stay legible as ink
   line art, which is what their presentation attributes are for.
   ========================================================================== */

/* --------------------------------------------------------------------------
   TILT
   Four steps, none of them zero. Apply to anything already carrying a border
   and a shadow; the tilt is only convincing on an element that reads as a
   solid object.
   -------------------------------------------------------------------------- */
.nb-tilt-1 {
  transform: rotate(var(--nb-tilt-1));
}

.nb-tilt-2 {
  transform: rotate(var(--nb-tilt-2));
}

.nb-tilt-3 {
  transform: rotate(var(--nb-tilt-3));
}

.nb-tilt-4 {
  transform: rotate(var(--nb-tilt-4));
}

/* A pressable element that is also tilted has to keep its tilt through the
   press, or it snaps square at the moment of contact. */
.nb-btn.nb-tilt-1:active:not(:disabled) {
  transform: rotate(var(--nb-tilt-1))
    translate(var(--nb-press-travel), var(--nb-press-travel));
}

.nb-btn.nb-tilt-4:active:not(:disabled) {
  transform: rotate(var(--nb-tilt-4))
    translate(var(--nb-press-travel), var(--nb-press-travel));
}

/* --------------------------------------------------------------------------
   MOTIFS
   The extruded solids. Paint rules are scoped per motif so each one carries
   its own light model: one lit face, one shaded face, one front.
   -------------------------------------------------------------------------- */
.nb-motif {
  display: block;
  width: var(--nb-motif-size);
  height: auto;
  color: var(--nb-ink);
}

.nb-motif .nb-art-front,
.nb-motif .nb-art-top,
.nb-motif .nb-art-side {
  stroke: var(--nb-ink);
}

.nb-motif-cube .nb-art-top {
  fill: var(--nb-surface);
}

.nb-motif-cube .nb-art-front {
  fill: var(--nb-fill-info);
}

.nb-motif-cube .nb-art-side {
  fill: var(--nb-primary);
}

.nb-motif-wedge .nb-art-top {
  fill: var(--nb-fill-warning);
}

.nb-motif-wedge .nb-art-front {
  fill: var(--nb-accent);
}

.nb-motif-stack .nb-art-top {
  fill: var(--nb-surface);
}

.nb-motif-stack .nb-art-front {
  fill: var(--nb-fill-success);
}

.nb-motif-stack .nb-art-side {
  fill: var(--nb-fill-success);
}

/* A cluster, not a row. The gap is negative so the solids overlap and read as
   a pile; spaced evenly they would read as an icon set. */
.nb-motif-cluster {
  display: flex;
  align-items: flex-end;
  gap: var(--nb-motif-cluster-gap);
}

.nb-motif-cluster > * {
  flex: none;
}

/* --------------------------------------------------------------------------
   SEAL
   The burst sticker. Geometry and label are separate, so the label is real
   text: selectable, translatable, and legible to a screen reader.
   -------------------------------------------------------------------------- */
.nb-seal {
  position: relative;
  display: inline-grid;
  place-items: center;
  width: var(--nb-seal-size);
  height: var(--nb-seal-size);
  transform: rotate(var(--nb-tilt-1));
}

.nb-seal > .nb-motif-burst {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

.nb-motif-burst .nb-art-front {
  fill: var(--nb-fill-warning);
  stroke: var(--nb-ink);
}

/* The offset copy under the seal is its shadow, drawn as geometry so it stays
   hard-edged at any scale. */
.nb-motif-burst .nb-art-cast {
  fill: var(--nb-ink);
}

.nb-seal-label {
  position: relative;
  /* A fixed share of the seal rather than a max-width: the label has to wrap
     inside the points of the burst, and max-content would run past them. */
  width: 66%;
  text-align: center;
  overflow-wrap: break-word;
  font-family: var(--nb-font-sans);
  font-size: var(--nb-text-xs);
  font-weight: var(--nb-weight-display);
  line-height: var(--nb-leading-snug);
  text-transform: uppercase;
  color: var(--nb-ink);
}

/* Hang the seal off a container corner. This is the token that separates a
   sticker from a badge: inside the box it is chrome, over the edge it is an
   object someone stuck on afterward. */
.nb-seal-corner {
  position: absolute;
  top: calc(var(--nb-sticker-overlap) * -1);
  right: calc(var(--nb-sticker-overlap) * -1);
  z-index: 2;
}

/* --------------------------------------------------------------------------
   STICKER TREATMENT
   Turns an existing badge into something stamped on. Heavier border, a real
   shadow instead of the small one, and a tilt.
   -------------------------------------------------------------------------- */
.nb-sticker {
  transform: rotate(var(--nb-tilt-4));
  padding: var(--nb-space-2) var(--nb-space-3);
  border-width: var(--nb-border-width-heavy);
  box-shadow: var(--nb-shadow);
}

.nb-sticker-left {
  transform: rotate(var(--nb-tilt-1));
}

/* --------------------------------------------------------------------------
   HAND DRAWN MARKS
   Ink, never a fill color. These read as something added on top of the layout
   by a person, and a colored pen would make them part of the system again.
   -------------------------------------------------------------------------- */
.nb-mark {
  display: block;
  color: var(--nb-ink);
}

.nb-mark .nb-art-line {
  stroke: currentColor;
}

/* Underline a word inside a heading. The wrapper keeps the word on one line so
   the squiggle never straddles a line break. */
.nb-marked {
  position: relative;
  display: inline-block;
  white-space: nowrap;
}

.nb-marked > .nb-mark {
  position: absolute;
  left: 0;
  bottom: -0.16em;
  width: 100%;
  height: auto;
  pointer-events: none;
}

/* A free-standing mark, for arrows that point from one block to another. */
.nb-mark-float {
  width: var(--nb-motif-size);
  height: auto;
}

/* --------------------------------------------------------------------------
   TAPE
   The section divider. Sits on the seam between two bands and straddles it.
   The parent section needs position: relative, and the page needs
   overflow-x: clip, because the rotated strip runs past both edges by design.
   -------------------------------------------------------------------------- */
/* A host that carries a tape stops drawing its own top border. The tape is the
   seam; a horizontal rule sitting behind a rotated strip pokes out past the
   strip's edge at the ends and reads as a rendering fault rather than as a
   border. The two bands still differ in background, so the boundary stays
   legible without a rule on it.

   :has() rather than a modifier class, so the rule cannot be forgotten when
   someone adds a tape. It is Baseline widely available (Chrome and Edge 105,
   Safari 15.4, Firefox 121, so December 2023). Where it is unsupported the
   stray rule comes back and nothing else changes, which is a cosmetic
   degradation rather than a break. If you need to support older engines, put a
   class on the host and set border-top: none on it instead.

   This is a default, and a consumer stylesheet that sets its own border on the
   host at equal specificity will win on source order. Restate it at your own
   specificity when that happens, the way demo/index.html does. */
:has(> .nb-tape) {
  border-top: none;
}

.nb-tape {
  position: absolute;
  top: 0;
  left: calc(var(--nb-tape-bleed) * -1);
  right: calc(var(--nb-tape-bleed) * -1);
  z-index: 3;

  display: flex;
  align-items: center;
  justify-content: center;
  height: var(--nb-tape-height);

  font-family: var(--nb-font-mono);
  font-size: var(--nb-text-sm);
  font-weight: var(--nb-weight-bold);
  letter-spacing: var(--nb-tracking-wide);
  text-transform: uppercase;
  color: var(--nb-accent-ink);

  background: var(--nb-accent);
  border-top: var(--nb-border-width-heavy) solid var(--nb-border-color);
  border-bottom: var(--nb-border-width-heavy) solid var(--nb-border-color);
  transform: translateY(-50%) rotate(var(--nb-tilt-2));
}

/* The tape carries a sentence, and a sentence centred on a strip narrower than
   itself loses a word off each end rather than wrapping. Drop a step at the
   phone breakpoint, where the strip is around 400px and the sentence is not. */
@media (max-width: 767px) {
  .nb-tape {
    font-size: var(--nb-text-xs);
  }
}

/* Nothing in this file animates. The tilts are static geometry rather than
   motion, so prefers-reduced-motion has nothing to switch off here. */
