/* ============================================================
   Bifrost — HOUSE layer  (Bifrost 2.0)
   ------------------------------------------------------------
   What every Bifrost entity shares: the typeface files, the
   three background plates, the arch motif, the logo lockup
   mechanics, and the bf-* components.

   What it does NOT set: ink, ink tints, accent, and — new in
   2.0 — the DISPLAY TYPEFACE. Bifrost Forge sets its titles in
   GT Standard while Group, Studios and Partners set theirs in
   Concrette M, so --font-display and --tr-display belong to the
   sub-brand layer.

   Load order:
     tokens.css → primitives.css → <medium>-core.css
                → bifrost/_house.css → bifrost/<sub>.css

   The sub-brand file loads last and wins.

   Sub-brands: group.css · studios.css · forge.css · partners.css
               holdings.css is a deprecated alias of group.css.
   ============================================================ */

/* ============================================================
   FONTS — LICENSED
   ------------------------------------------------------------
   Licensed 7 Aug 2026. Every TRIAL file has been removed.

   Concrette M — Displaay Type Foundry, invoice DP15931.
     Licensed: Desktop + Print, Social Media, Web. Up to 6 workers.
     Licensee of record: Bifrost Studios UK Ltd.
     Weights licensed: REGULAR (400) ONLY. No italic.
     This is exactly the weight the 2.0 guidelines specify for titles
     and exactly what the trial set was missing.

   GT Standard L — Grilli Type.
     Weights licensed: Light (300) and Medium (500), each with an
     Oblique. NO Semibold.

   ⚠ READ brand.md BEFORE EMBEDDING THESE ANYWHERE.
   The Grilli Type EULA permits embedding into PDF, PS and EPS only.
   Base64-inlining the fonts into a shareable HTML file is NOT
   covered. scripts/bundle.mjs therefore does not embed fonts by
   default; the licensed way to share an artifact is the PDF.
   ============================================================ */

/* ---------- Concrette M — display face, Regular only ---------- */
@font-face {
  font-family: "Concrette";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("./fonts/ConcretteM-Regular.woff2") format("woff2"),
       url("./fonts/ConcretteM-Regular.otf")   format("opentype");
}

/* ---------- GT Standard L — body face, and Forge's display ----
   Light 300 for prose, Medium 500 for labels, eyebrows and deck
   body. Obliques are licensed, so <em> is real italic, not a
   synthesised slant. */
@font-face {
  font-family: "GT Standard";
  font-style: normal;
  font-weight: 300;
  font-display: swap;
  src: url("./fonts/GT-Standard-L-Standard-Light.woff2") format("woff2"),
       url("./fonts/GT-Standard-L-Standard-Light.otf")   format("opentype");
}
@font-face {
  font-family: "GT Standard";
  font-style: italic;
  font-weight: 300;
  font-display: swap;
  src: url("./fonts/GT-Standard-L-Standard-Light-Oblique.woff2") format("woff2"),
       url("./fonts/GT-Standard-L-Standard-Light-Oblique.otf")   format("opentype");
}
@font-face {
  font-family: "GT Standard";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("./fonts/GT-Standard-L-Standard-Medium.woff2") format("woff2"),
       url("./fonts/GT-Standard-L-Standard-Medium.otf")   format("opentype");
}
@font-face {
  font-family: "GT Standard";
  font-style: italic;
  font-weight: 500;
  font-display: swap;
  src: url("./fonts/GT-Standard-L-Standard-Medium-Oblique.woff2") format("woff2"),
       url("./fonts/GT-Standard-L-Standard-Medium-Oblique.otf")   format("opentype");
}

/* ---------- GT Standard M and S — the optical sizes -----------
   GT Standard ships three optical sizes and we hold all three. The
   2.0 guidelines say "GT Standard L" everywhere, so L is the default
   and nothing below is wired up by default.

   They are declared because optical sizes are the point of a family
   that has them: L is cut for display, S for small text. Our document
   body runs at 20px and captions at 17px, which is what S is designed
   for. Using them would be a small, real quality gain.

   That is a brand decision, not an implementation one — raise it with
   Torsten rather than switching silently. To try it, set
   --font-sans-s on a block. woff2 only; these are for screen.

   NOTE: bundle.mjs inlines only what CSS actually references, so
   declaring these costs a shipped artifact nothing. */
@font-face {
  font-family: "GT Standard M";
  font-style: normal; font-weight: 300; font-display: swap;
  src: url("./fonts/optical/GT-Standard-M-Standard-Light.woff2") format("woff2");
}
@font-face {
  font-family: "GT Standard M";
  font-style: normal; font-weight: 500; font-display: swap;
  src: url("./fonts/optical/GT-Standard-M-Standard-Medium.woff2") format("woff2");
}
@font-face {
  font-family: "GT Standard S";
  font-style: normal; font-weight: 300; font-display: swap;
  src: url("./fonts/optical/GT-Standard-S-Standard-Light.woff2") format("woff2");
}
@font-face {
  font-family: "GT Standard S";
  font-style: normal; font-weight: 500; font-display: swap;
  src: url("./fonts/optical/GT-Standard-S-Standard-Medium.woff2") format("woff2");
}

/* Never let the browser fake a weight or a slant. Concrette has one
   licensed weight and no italic; GT Standard has no Semibold. Without
   this, asking for 600 or for italic Concrette produces a synthesised
   face — a smeared fake bold or a mechanical slant — which looks
   wrong and misrepresents the typeface. With it, the request falls
   back to the nearest real face instead. */
* { font-synthesis: none; }

/* ============================================================
   Shared contract values
   ------------------------------------------------------------
   The three background plates and the body face are house-wide.
   Ink, tints, accent and the display face are per sub-brand.
   ============================================================ */
:root {
  /* Raw house palette. Sub-brands may read these; other brands
     must never reference them (see BRAND-PACK.md rule 1). */
  --bf-base:   #DED9D6;
  --bf-core:   #EFEBE9;
  --bf-high:   #F4F1EF;

  --c-bg-base: var(--bf-base);
  --c-bg-core: var(--bf-core);
  --c-bg-high: var(--bf-high);

  --font-sans: "GT Standard", -apple-system, BlinkMacSystemFont,
               "Segoe UI", system-ui, sans-serif;

  /* The optical sizes, available but not default. See the @font-face
     note above before reaching for them. */
  --font-sans-m: "GT Standard M", var(--font-sans);
  --font-sans-s: "GT Standard S", var(--font-sans);

  /* Wordmark face. The 2.0 lockup wordmark is Akke Poster Big Light,
     which we do NOT hold as a font file — so the house never sets type
     in it. Every wordmark in this system is the outlined asset
     assets/bifrost-wordmark.svg, painted through a CSS mask.
     --font-logo stays pointed at the display face purely as a fallback
     for anything that ignores the asset. */
  --font-logo: var(--font-display);

  /* The wordmark's aspect ratio, published to the contract so a medium
     engine can size a wordmark slot without knowing the brand. The shape
     itself arrives as an inline SVG sprite (see the lockup notes below). */
  --wordmark-ratio: 345.75 / 96.25;

  /* Bifrost is a square system. Corner rounding is a Studios /
     Forge differentiator on the WEBSITE only; the deck and
     document templates measured 0 on every plate, table and
     photo frame. Sub-brands raise --radius-web, never
     --radius-card. */
  --radius-card: 0;
  --radius-soft: 4px;
}

/* Body prose runs at Medium in the Bifrost family — GT Standard
   Light is too fine for running text at deck scale. Display
   stays at the weight the sub-brand sets. */
.slide p,
.doc-page p,
.bf-prose { font-weight: 500; }

/* ============================================================
   ARCH — the house motif
   ------------------------------------------------------------
   The arch is not a logo and not an image: it is a field of
   nested circle outlines, massively oversized and clipped by
   its container, so only the springing arcs read. It appears
   on deck section openers, document covers, and social banners.

   Geometry, from the Figma source: N circles of equal radius on
   a common centre-line, stepped by a constant horizontal pitch.
   Every parameter is a custom property so one component covers
   the deck band, the document cover field and the timeline.

     --arch-n      how many circles (default 9)
     --arch-r      circle radius
     --arch-cy     centre-line y, relative to the arch box
     --arch-cx     centre x of the FIRST circle
     --arch-pitch  x step between circles
     --arch-ink    stroke colour   (default: Ink/100%)
     --arch-ghost  stroke colour for the alternating faint arcs

   Markup: <div class="bf-arch" aria-hidden="true"></div> with
   nine <i> children, or use the .bf-arch--9 shorthand which
   generates them from a repeating conic-free box-shadow-free
   pure-border technique below. The <i> form is preferred: it
   survives PDF export in every engine we tested.
   ============================================================ */
.bf-arch {
  --arch-n:      9;
  --arch-r:      1157px;
  --arch-cy:     1047px;
  --arch-cx:     97px;
  --arch-pitch:  211px;
  --arch-ink:    var(--c-ink);
  --arch-ghost:  var(--c-ink-4);

  position: absolute;
  inset: 0;
  overflow: hidden;          /* the clip IS the design */
  pointer-events: none;
}

.bf-arch > i {
  position: absolute;
  display: block;
  width:  calc(var(--arch-r) * 2);
  height: calc(var(--arch-r) * 2);
  top:  calc(var(--arch-cy) - var(--arch-r));
  left: calc(var(--arch-cx) - var(--arch-r)
             + var(--arch-pitch) * var(--i, 0));
  border: var(--rule-w) solid var(--arch-ink);
  border-radius: 50%;
  box-sizing: border-box;
}

/* Index the children so each steps one pitch to the right. */
.bf-arch > i:nth-child(1) { --i: 0; }
.bf-arch > i:nth-child(2) { --i: 1; }
.bf-arch > i:nth-child(3) { --i: 2; }
.bf-arch > i:nth-child(4) { --i: 3; }
.bf-arch > i:nth-child(5) { --i: 4; }
.bf-arch > i:nth-child(6) { --i: 5; }
.bf-arch > i:nth-child(7) { --i: 6; }
.bf-arch > i:nth-child(8) { --i: 7; }
.bf-arch > i:nth-child(9) { --i: 8; }
.bf-arch > i:nth-child(10) { --i: 9; }
.bf-arch > i:nth-child(11) { --i: 10; }
.bf-arch > i:nth-child(12) { --i: 11; }

/* Alternating weight: solid on the odd arcs, ghost on the even
   ones. This is the timeline's rhythm — one solid arc marks
   each stop, the faint arcs are texture between them. */
.bf-arch--alternating > i:nth-child(even) { border-color: var(--arch-ghost); }

/* All-ghost field, for backgrounds that must not compete. */
.bf-arch--ghost > i { border-color: var(--arch-ghost); }

/* The document-cover variant: wider circles, tighter pitch. */
.bf-arch--doc {
  --arch-r:     1158px;
  --arch-pitch: 211px;
  --arch-cy:    -466px;
  --arch-cx:    -283px;
}

/* The timeline variant: one radius, wide pitch, centre low. */
.bf-arch--timeline {
  --arch-r:     1266.55px;
  --arch-pitch: 255.44px;
  --arch-cy:    1002.9px;
  --arch-cx:    1294.75px;
}

/* ============================================================
   LOGO LOCKUPS
   ------------------------------------------------------------
   Naming: bifrost-<sub>-<form>[-<colourway>].svg
     form:       logo | logo-abbr | mark
     colourway:  (none = full colour) | mono-dark | mono-light
                 | inverted  (Partners only)

   Group has no abbreviated form — "Bifrost" is already short.
   Only Group ships monochrome MARK variants; Studios, Forge and
   Partners use their full or abbreviated logo instead.
   Partners has a third state, `inverted`: dark green on a light
   ground, for contracts and other serious paperwork where the
   dark-green house background is impractical.

   Mechanics: the markup ships BOTH the dark and the light
   colourway; CSS reveals whichever reads on the current surface.
   ============================================================ */
/* --- The single-file lockup (PREFERRED) ---------------------
   Each colourway ships as one complete SVG with the brand's own colours
   baked in, so the simplest and most robust lockup is one <img>:

       img.bf-logo  src=".../bifrost-group-logo.svg"  alt="Bifrost"

   Ship both colourways and let CSS pick the one that reads on the current
   surface:

       img.bf-logo.bf-logo--dark   src=".../bifrost-group-logo-mono-dark.svg"
       img.bf-logo.bf-logo--light  src=".../bifrost-group-logo-mono-light.svg"

   Forms: -logo (full) · -logo-abbr (abbreviated) · -mark (logomark).
   Colourways: none (full colour) · -mono-dark · -mono-light · -inverted
   (Partners only). Group has no abbreviated form; only Group ships
   monochrome mark variants.                                            */
.bf-logo { height: 22px; width: auto; display: block; }
.bf-logo--light { display: none; }
.slide--dark .bf-logo--dark,
.bf-on-ink   .bf-logo--dark { display: none; }
.slide--dark .bf-logo--light,
.bf-on-ink   .bf-logo--light { display: block; }

/* Legacy aliases, so 1.0 markup keeps working. */
.bf-wordmark { display: inline-flex; align-items: center; line-height: 1; }
.bf-wordmark__logo { height: 22px; width: auto; display: block; }
.bf-wordmark__logo--light { display: none; }
.slide--dark .bf-wordmark__logo--dark,
.bf-on-ink   .bf-wordmark__logo--dark { display: none; }
.slide--dark .bf-wordmark__logo--light,
.bf-on-ink   .bf-wordmark__logo--light { display: block; }

/* --- The composed lockup (mark + wordmark) -----------------
   Use only when you need to scale the two parts independently, or when the
   wordmark has to take a colour no exported file carries.

   The wordmark here is INLINE SVG, not an <img> and not a CSS mask. Both
   of those were tried and both fail: an <img>-loaded SVG cannot see the
   page's CSS, so `currentColor` inside it resolves to black and every
   non-black brand (Forge's orange, Partners' bone) comes out wrong; and
   Chromium refuses to apply an SVG mask-image loaded over file://, so the
   wordmark silently disappears until the artifact is bundled. Inline SVG
   with currentColor works in every case — unbundled, bundled, and printed.

   Drop the sprite once per artifact (see templates/), then:
       span.bf-lockup  >  img.bf-lockup__mark  src=".../bifrost-group-mark.svg"
                          +  svg.bf-lockup__word  >  use href="#bf-wordmark"

   Lockup proportions from the Figma source: the mark-to-word gap is
   0.368 x the mark height at every size, so the lockup scales from a 40px
   header to a 620px hero without re-spacing.                            */
.bf-lockup {
  --lockup-h: 87px;
  display: inline-flex;
  align-items: center;
  gap: calc(var(--lockup-h) * 0.368);
  line-height: 1;
}
.bf-lockup__mark { height: var(--lockup-h); width: auto; display: block; }
.bf-lockup__word {
  height: calc(var(--lockup-h) * 1.106);   /* 96.25 / 87 */
  aspect-ratio: var(--wordmark-ratio);
  display: block;
  color: var(--c-ink);
  fill: currentColor;
}
.slide--dark .bf-lockup__word,
.bf-on-ink   .bf-lockup__word { color: var(--c-bg-base); }

/* Hero lockup — the cover slide. Optically centred; the Figma source is
   0.4px off true centre, which is not worth chasing. */
.bf-hero { position: absolute; inset: 0; display: grid; place-items: center; }
.bf-hero .bf-lockup { --lockup-h: 87px; }

/* Display watermark — the outlined wordmark set enormous and dropped to
   Ink/Tint 4, as on the standalone contents slide. Inline SVG for the same
   reason as the lockup wordmark.
   Note the glyph's 16px left side bearing: the watermark is NOT optically
   flush with a text column on the same margin, so it is pulled 16px left. */
.bf-watermark {
  display: block;
  aspect-ratio: var(--wordmark-ratio);
  color: var(--c-ink-4);
  fill: currentColor;
  margin-left: -16px;
}

/* ============================================================
   ICONS
   ------------------------------------------------------------
   22 icons from the Master UI file (COMPONENTS & ASSETS page),
   shipped as ONE inline sprite: assets/bifrost-icons.svg.
   Drop the sprite once per artifact, then:

       <svg class="bf-icon"><use href="#bf-i-search"/></svg>

   9 UI icons:     chevron-down circle search arrow-right arrow-left
                   plus cross external-arrow burger
   13 sector icons: consumer commerce gaming media cpg data
                   cybersecurity dev saas defense web3 ai robot

   The sector set maps 1:1 onto the mosaic categories, so a
   sector-exposure slide can now carry its own icons.

   Every path is currentColor, so an icon takes the brand ink —
   black for Group and Studios, orange for Forge, bone for
   Partners — with no per-brand asset. That is why these are
   inline SVG and not <img>: an <img>-loaded SVG cannot see the
   page's CSS. Same reason as the wordmark.

   Source geometry is a 24x24 grid with a uniform 1-unit stroke,
   passed through at scale 1.0. Do NOT rescale individual symbols
   to "fill" the box — the ink deliberately occupies 9.4 to 16.25
   of the 24 units, and rescaling per icon is what breaks stroke
   consistency across the set. Size the .bf-icon box instead.
   ============================================================ */
.bf-icon {
  width: 1.5em;
  height: 1.5em;
  display: inline-block;
  vertical-align: middle;
  color: inherit;
  fill: none;
  stroke: currentColor;
  flex: none;
}
/* Sized variants. The box grows; the symbol never rescales. */
.bf-icon--sm { width: 1em;    height: 1em; }
.bf-icon--lg { width: 2.25em; height: 2.25em; }
.bf-icon--xl { width: 4rem;   height: 4rem; }

/* Sector icons sit above a mosaic figure at the same optical
   weight as the rules around them. */
.bf-mosaic__cell .bf-icon { margin-bottom: var(--s-2); }

/* ============================================================
   bf-* COMPONENTS
   ------------------------------------------------------------
   Brand-specific primitives, prefixed so they never collide
   with the shared medium primitives.
   ============================================================ */

/* Area mosaic — the sector-exposure table. Cell AREA encodes
   the quantity; the numeral is the readout, not the chart.
   Structural 2px rules, shared between neighbouring cells. */
.bf-mosaic {
  display: flex;
  border: var(--rule-w) solid var(--c-ink);
  box-sizing: border-box;
}
.bf-mosaic__col { display: flex; flex-direction: column; }
.bf-mosaic__col + .bf-mosaic__col { border-left: var(--rule-w) solid var(--c-ink); }
.bf-mosaic__cell {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding: var(--s-4);
  box-sizing: border-box;
  min-height: 91px;     /* floor: 20 pad + 35 figure + 16 label + 20 pad */
}
.bf-mosaic__cell + .bf-mosaic__cell { border-top: var(--rule-w) solid var(--c-ink); }
.bf-mosaic__figure {
  font-family: var(--font-sans);
  font-weight: 300;
  font-size: var(--fs-figure);
  line-height: var(--lh-snug);
  color: var(--c-ink);
}
.bf-mosaic__label {
  font-family: var(--font-sans);
  font-weight: 300;
  font-size: var(--fs-body);
  line-height: var(--lh-snug);
  color: var(--c-ink-3);
}

/* Dot matrix — venture-building. Outlined cards of outlined
   dots: countable capacity, not a chart. */
.bf-matrix { display: flex; gap: 10px; }
.bf-matrix__col {
  flex: 1;
  border: var(--rule-w-fine) solid var(--c-ink-4);
  border-radius: 6px;
  padding: 35px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  box-sizing: border-box;
}
.bf-matrix__row { display: flex; justify-content: space-between; }
.bf-matrix__dot {
  width: 20px; height: 20px;
  border: var(--rule-w-fine) solid var(--c-ink);
  border-radius: var(--radius-pill);
  box-sizing: border-box;
}

/* Bubble field — venture-buying. Circles of unequal size over a
   stepped guide scaffold; bubbles are filled with the ground so
   they knock the scaffold out. */
.bf-bubbles { position: relative; border: var(--rule-w-fine) solid var(--c-ink-4); }
.bf-bubbles__guide {
  position: absolute; top: 0; left: 0;
  border-right:  var(--rule-w-fine) solid var(--c-ink-4);
  border-bottom: var(--rule-w-fine) solid var(--c-ink-4);
  border-bottom-right-radius: 5px;
}
.bf-bubbles__b {
  position: absolute;
  border: var(--rule-w-fine) solid var(--c-ink);
  border-radius: var(--radius-pill);
  background: var(--c-bg-base);
  box-sizing: border-box;
}

/* Table — hairline, editorial. Structural 2px above and below
   the header, nothing else. */
.bf-table { width: 100%; border-collapse: collapse; font-size: var(--fs-body); }
.bf-table th {
  text-align: left;
  font-weight: 500;
  color: var(--c-ink-3);
  padding: var(--s-3) 0;
  border-bottom: var(--rule-w) solid var(--c-ink);
}
.bf-table td {
  padding: var(--s-3) 0;
  border-bottom: var(--rule-w-fine) solid var(--c-ink-4);
  vertical-align: top;
}
.bf-table tr:last-child td { border-bottom: 0; }
.bf-table .num { text-align: right; font-variant-numeric: tabular-nums; }

/* Story accent — the one place the Bifrost documents use accent:
   a 2px accent rule beside a section heading, an accent lead-in
   line, and accent links. Deliberately scarce. */
.bf-story { display: flex; align-items: center; gap: var(--s-3); padding-left: 2px; }
.bf-story::before {
  content: "";
  flex: none;
  width: 2px; height: 36px;
  background: var(--c-accent);
}
.bf-story__lead { font-weight: 500; color: var(--c-accent); }
.bf-prose a {
  color: var(--c-accent);
  text-decoration: underline;
  text-underline-position: from-font;
  text-decoration-skip-ink: none;
}

/* Halftone portrait — the team-page treatment. In Figma this is
   a vibe-coded shader (dot size, gradient map, scatter). In CSS
   we approximate with a duotone screen; the effect per brand is
   black-and-white for Group, green for Partners, full colour for
   Studios and Forge, so the filter is a brand-set token. */
.bf-portrait {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-soft);
  background: var(--c-bg-core);
}
.bf-portrait img {
  display: block; width: 100%; height: 100%;
  object-fit: cover;
  filter: var(--bf-portrait-filter, grayscale(1) contrast(1.08));
}
.bf-portrait::after {          /* the dot screen */
  content: "";
  position: absolute; inset: 0;
  background-image: radial-gradient(var(--c-ink) 22%, transparent 23%);
  background-size: 4px 4px;
  mix-blend-mode: overlay;
  opacity: var(--bf-portrait-dots, 0.35);
}
