/* ══════════════════════════════════════════════════════════════════
   fenmore — design tokens as CSS custom properties
   GENERATED FROM tokens.json by build.py. Do not hand-edit.

   Scoped to .fenmore so it can never leak into the portfolio's own
   styles. Wrap any mock screen in <div class="fenmore"> and it
   inherits everything below.
   ══════════════════════════════════════════════════════════════════ */

.fenmore{

  /* ─── PRIMITIVES — the only layer you edit to reskin ─── */
  --fen-primitive-chrome-700:        #3A352C;  /* Raised on the inverse — chip hover, popover over the bar. */
  --fen-primitive-chrome-800:        #25231D;  /* The nav bar. WARM CHARCOAL, hue 40 — one degree off the cream, so chrome and content read as the same family. */
  --fen-primitive-chrome-900:        #1A1814;  /* The category panel and footer. */
  --fen-primitive-accent-50:         #F3F8F9;  /* Ghost-hover wash. Lighter than /100. */
  --fen-primitive-accent-100:        #ECF3F4;  /* Soft tint, and surface/tint — so the gate measures against it as a light surface. */
  --fen-primitive-accent-200:        #CBDFE1;  /* Deeper tint. Selected rows and badge fills on light, where /100 is too faint to read as a state. */
  --fen-primitive-accent-300:        #95C1C6;  /* PLACED, not solved. Interactive on the inverse. Solved as a boundary this came out at L48 — below accent/400 — because on a dark chrome the constraint is not binding and the lowest passing value is not the right one. */
  --fen-primitive-accent-400:        #7EAEB4;  /* FILL ONLY on light. */
  --fen-primitive-accent-500:        #58969D;  /* The mid-tone. Decorative fills and borders that must read as TEAL rather than as grey — /400 is too pale and /600 is committed to interactive. */
  --fen-primitive-accent-600:        #407277;  /* SOLVED — default interactive. TEAL: the only hue that appears in the chrome. Also carries success, since the system has no second signal colour. */
  --fen-primitive-accent-700:        #2F5B60;  /* Pressed / hover. */
  --fen-primitive-accent-800:        #21464A;  /* Deep teal. High-contrast text on light (a heading colour on cream) and the business card ground. */
  --fen-primitive-accent-900:        #143033;  /* Deepest. A teal-tinted inverse surface, and the darkest card ground — so a teal card face no longer has to borrow from chrome. */
  --fen-primitive-brand-100:         #EAF5EB;  /* Faintest wash. */
  --fen-primitive-brand-200:         #D1EBD4;  /* Soft fill. */
  --fen-primitive-brand-300:         #B0DEB7;  /* FILL ONLY on light. */
  --fen-primitive-brand-400:         #73C980;  /* FILL ONLY on light. */
  --fen-primitive-brand-500:         #3FAB51;  /* SOLVED — the identity green. Text on charcoal, and the selected-chip fill. GREEN IS NO LONGER THE GROUND: it appears as punctuation only. */
  --fen-primitive-brand-600:         #2D9542;  /* Fill and border. */
  --fen-primitive-brand-700:         #1D7732;  /* DEAD ZONE — fails as text on every surface, which is what makes it the right border AND the right sheen band for the premium card: a fill that has to hold a white label, not a value that has to be read. Do not lighten it; at L36 the sheen drops to 3.90:1 and the gradient gate stops the build. */
  --fen-primitive-brand-800:         #186825;  /* SOLVED — green as TEXT ON LIGHT. Pushed darker than the first solve, which landed at L30 and came out LIGHTER than brand/700, inverting the ramp. The build now asserts monotonic lightness on every colour family, not just the neutrals. */
  --fen-primitive-neutral-0:         #FFFFFF;  /* Raised surfaces. */
  --fen-primitive-neutral-50:        #F8F7F4;  /* Page background. Warm cream. */
  --fen-primitive-neutral-100:       #F3F1ED;  /* Sunken. Deliberately close to /50 — a page and its wells are the same paper. */
  --fen-primitive-neutral-200:       #E0DDD7;  /* Hairline borders. */
  --fen-primitive-neutral-300:       #C9C5BA;  /* Strong borders on light; secondary text on the inverse. */
  --fen-primitive-neutral-350:       #A19D91;  /* PLACED, not solved. On the old forest chrome this was the tightest value in the system; charcoal has enough headroom (15.7:1 for white vs 12.9:1) that the third text tier stopped being binding — and solving a boundary that is no longer binding pushed it BELOW neutral/400 and inverted the ramp. */
  --fen-primitive-neutral-400:       #948F7F;  /* Decorative on the inverse. */
  --fen-primitive-neutral-500:       #837D6D;  /* The mid-tone hinge. */
  --fen-primitive-neutral-600:       #736D5E;  /* SOLVED — muted text, boundary on the worst light surface. */
  --fen-primitive-neutral-700:       #605B4D;  /* Secondary text. */
  --fen-primitive-neutral-900:       #201E18;  /* Primary text. Warm near-black. */
  --fen-primitive-forest-500:        #27684D;  /* CARD ART ONLY. Mid forest. */
  --fen-primitive-forest-700:        #143D2C;  /* CARD ART ONLY. The business card ground. */
  --fen-primitive-forest-900:        #0C271C;  /* CARD ART ONLY. Deepest green — the premium ground. Green left the UI but has to stay in the cards, which is what this family is for. */

  /* ─── SEMANTIC COLOUR — components reference these ─── */
  --fen-color-surface-page:          #F8F7F4;
  --fen-color-surface-raised:        #FFFFFF;
  --fen-color-surface-sunken:        #F3F1ED;
  --fen-color-surface-tint:          #ECF3F4;  /* Chip and badge backgrounds. */
  --fen-color-surface-chrome:        #25231D;
  --fen-color-surface-chrome-deep:   #1A1814;
  --fen-color-surface-selected:      #CBDFE1;  /* Selected row or card on a light surface. accent/100 is the passive tint; this is the active one, and the pair is what lets a list show selection without a fill that shouts. CAVEAT: /200 against /100 is 1.23:1 — below the 3:1 non-text threshold, which is correct for a selected row (a shouty fill is wrong here) but means the row needs a SECOND cue. Pair it with border/selected or a check, never colour alone. */
  --fen-color-text-primary:          #201E18;  /* Passes on all four light surfaces. */
  --fen-color-text-secondary:        #605B4D;  /* Passes on all four light surfaces. */
  --fen-color-text-muted:            #736D5E;  /* Passes on all four light surfaces. Metadata, placeholders. */
  --fen-color-text-on-chrome:        #FFFFFF;  /* DEPRECATED — kept so nothing breaks. This token names the surface it sits on, which forces every component that lands on chrome to have a variant. Use .fen-inverse and text/primary instead. */
  --fen-color-text-on-accent:        #FFFFFF;  /* Text on a filled interactive surface. 5.3:1 on accent/600. */
  --fen-color-text-brand:            #3FAB51;  /* ONLY on chrome. Fill-only on light surfaces. */
  --fen-color-text-success:          #2F5B60;  /* RETIRED AS A SEPARATE HUE. The old success green sat 28 degrees from the new accent, and two greens 28 degrees apart is not a signal. In a green system green cannot mean "approved", so success is the accent value and the differentiation must come from a non-colour cue — a mark, a label, a position. */
  --fen-color-text-accent-strong:    #21464A;  /* Deep teal as a heading on light. The accent family previously had no value that could hold large type on cream — /600 clears 4.5:1 but reads as a link, not a heading. */
  --fen-color-border-default:        #E0DDD7;
  --fen-color-border-strong:         #C9C5BA;
  --fen-color-border-focus:          #407277;
  --fen-color-interactive-default:   #407277;
  --fen-color-interactive-hover:     #2F5B60;
  --fen-color-interactive-subtle:    #F3F8F9;  /* Ghost hover wash. Was accent/100, which dropped the ghost label to 4.19:1 — a hover state that made its own text illegible. accent/50 holds it at 4.75:1. */
  --fen-color-interactive-fill:      #7EAEB4;  /* Decorative fills only. Never carries text. */
  --fen-color-selected-fill:         #3FAB51;  /* Selected chip and row fill. Chartreuse, on both surfaces — its one job. */
  --fen-color-selected-text:         #1A1814;  /* 7.5:1 on the fill. */
  --fen-color-selected-edge:         #1D7732;  /* The dead-zone value. A chartreuse pill on white is 1.8:1 and has no visible edge without it; 3.6:1 clears the non-text threshold. */

  /* ─── TYPE ─── */
  --fen-font-family-sans:            -apple-system, BlinkMacSystemFont, Segoe UI, system-ui, sans-serif;  /* Deliberately a system stack — fenmore is a placeholder and shouldn't have a distinctive voice. */
  --fen-font-weight-regular:         400;
  --fen-font-weight-medium:          500;
  --fen-font-weight-bold:            700;
  --fen-font-size-display:           32px;
  --fen-font-size-xl:                24px;
  --fen-font-size-lg:                20px;
  --fen-font-size-md:                16px;  /* Body. */
  --fen-font-size-sm:                14px;
  --fen-font-size-xs:                12px;
  --fen-font-size-micro:             11px;  /* Legal, disclosures, counts. */
  --fen-font-lineHeight-tight:       1.15;  /* Display and headings. */
  --fen-font-lineHeight-snug:        1.35;
  --fen-font-lineHeight-normal:      1.5;  /* Body. */
  --fen-font-letterSpacing-tight:    -2%;  /* Display. Large type needs negative tracking. */
  --fen-font-letterSpacing-snug:     -1.5%;  /* Headings and card names. */
  --fen-font-letterSpacing-normal:   0%;  /* Body and legal. */
  --fen-font-letterSpacing-wide:     1%;  /* Buttons and nav links — small text at medium weight. */
  --fen-font-letterSpacing-wider:    8%;  /* Uppercase labels. Caps need the room. */
  --fen-font-paragraphSpacing-none:  0px;
  --fen-font-paragraphSpacing-tight: 8px;  /* Dense blocks — card detail, table cells. */
  --fen-font-paragraphSpacing-normal: 16px;  /* Body copy. */
  --fen-font-paragraphSpacing-loose: 24px;  /* Lead paragraphs. */

  /* ─── SPACING — 4px base ─── */
  --fen-space-0:                     0px;
  --fen-space-1:                     4px;
  --fen-space-2:                     8px;
  --fen-space-3:                     12px;
  --fen-space-4:                     16px;
  --fen-space-5:                     24px;
  --fen-space-6:                     32px;
  --fen-space-7:                     48px;
  --fen-space-8:                     64px;

  /* ─── RADIUS ─── */
  --fen-radius-none:                 0px;
  --fen-radius-sm:                   3px;  /* Buttons, inputs. */
  --fen-radius-md:                   6px;  /* Cards, panels. */
  --fen-radius-lg:                   10px;
  --fen-radius-pill:                 999px;  /* Chips, badges. */

  /* ─── BORDER WIDTH ─── */
  --fen-borderWidth-hairline:        1px;
  --fen-borderWidth-thick:           2px;  /* Selected states, focus rings. */

  /* ─── BORDER — composite: width, style, colour ─── */
  --fen-border-default:              1px solid #E0DDD7;  /* Hairline. Cards, table rows, dividers. */
  --fen-border-strong:               1px solid #C9C5BA;  /* Emphasised. Inputs, secondary buttons. */
  --fen-border-focus:                2px solid #407277;  /* Focus ring. Never remove without replacing. */
  --fen-border-selected:             2px solid #407277;  /* Selected chip or card. */

  /* ─── OPACITY — three only; never on text ─── */
  --fen-opacity-disabled:            0.4;  /* Disabled controls. Never applied to text that must be read. */
  --fen-opacity-subtle:              0.6;  /* Decorative overlays on chrome. */
  --fen-opacity-scrim:               0.45;  /* Behind a modal or dropdown. */

  /* ─── SHADOW ─── */
  --fen-shadow-card:                 0px 1px 2px 0px #22222A14;
  --fen-shadow-dropdown:             0px 6px 20px -4px #22222A24;
  --fen-shadow-card-hover:           0px 6px 16px -2px #22222A29;  /* The lift on a product tile. Hover previously changed nothing, so a grid of clickable cards gave no feedback. */
  --fen-shadow-modal:                0px 24px 56px -12px #22222A57;  /* Deeper and wider than the dropdown so a dialog reads as a different layer, not a large menu. */
  --fen-shadow-sticky:               0px -4px 14px -2px #22222A29;  /* CAST UPWARD. A bottom-anchored bar throws its shadow up; reusing the downward card shadow here is the usual mistake and makes the bar look like it is floating on nothing. */
  --fen-shadow-inset:                inset 0px 1px 3px 0px #22222A24;  /* Sunken field or pressed toggle. The only inner shadow in the set. */

  /* ─── GRADIENTS — colour tokens holding CSS gradient strings ─── */
  --fen-gradient-card-standard:      linear-gradient(145deg, var(--fen-primitive-chrome-700) 0%, var(--fen-primitive-chrome-900) 100%);  /* Matte chrome. The default product, and the face most tiles show. */
  --fen-gradient-card-premium:       linear-gradient(135deg, var(--fen-primitive-forest-900) 0%, var(--fen-primitive-forest-700) 42%, var(--fen-primitive-brand-700) 52%, var(--fen-primitive-forest-700) 62%, var(--fen-primitive-forest-900) 100%);  /* Five stops, sheen confined to a ten-percent band. The sheen is brand/700 — the ramp dead zone, which fails as TEXT everywhere and is therefore exactly right as a FILL that has to hold a white label. brand/600 was tried first and the gate stopped the build at 3.83:1. */
  --fen-gradient-card-business:      linear-gradient(145deg, var(--fen-primitive-accent-800) 0%, var(--fen-primitive-accent-900) 100%);  /* A genuinely teal face now that the family reaches L14. It previously borrowed chrome/900 for its dark end, which meant the business card was really a charcoal card with a teal top. Teal is the interactive colour, so the business tier echoing it is deliberate. */
  --fen-gradient-card-starter:       linear-gradient(150deg, var(--fen-primitive-neutral-0) 0%, var(--fen-primitive-neutral-400) 100%);  /* Deliberately plain, and the only card face with an ink label. Entry cards look plain in every real marketplace; flattering it would misrepresent the hierarchy. */
  --fen-gradient-scrim-bottom:       linear-gradient(to top, rgba(26,24,20,.92) 0%, rgba(26,24,20,.72) 28%, rgba(26,24,20,.30) 52%, rgba(26,24,20,0) 78%);  /* Hero caption scrim. The 52% stop is where the scrim crosses 30% opacity, which is where white text clears 4.5:1 over a mid-grey photo — the text baseline must sit below it. That stop position IS the token; it is normally left in the head of whoever implemented it. Repinned to the new forest chrome/900 — these bake rgba() values because a hex reference cannot carry alpha, so a palette change has to update them by hand. That is the cost of the two alpha tokens. */
  --fen-gradient-scrim-top:          linear-gradient(to bottom, rgba(26,24,20,.85) 0%, rgba(26,24,20,.45) 45%, rgba(26,24,20,0) 100%);  /* For a nav floating over an image. Shorter and weaker than the bottom scrim — it only has to protect one row of links. Repinned to the new forest chrome/900 — these bake rgba() values because a hex reference cannot carry alpha, so a palette change has to update them by hand. That is the cost of the two alpha tokens. */
  --fen-gradient-fade-right:         linear-gradient(to right, rgba(255,255,255,0) 0%, var(--fen-color-surface-page) 70%);  /* Overflow affordance on a horizontally scrolling rail. Resolves against whichever surface it sits on, so it works on the inverse unchanged. */
  --fen-gradient-fade-left:          linear-gradient(to left, rgba(255,255,255,0) 0%, var(--fen-color-surface-page) 70%);  /* The same, for a rail scrolled away from its start. */
  --fen-gradient-tier:               linear-gradient(to right, var(--fen-primitive-brand-500), var(--fen-primitive-accent-600));  /* A 4px band above a featured row, so the featured state does not need a badge competing with the chips. The only two-hue blend in the system. */
  --fen-gradient-chrome-depth:       linear-gradient(to bottom, var(--fen-primitive-chrome-700), var(--fen-primitive-chrome-800));  /* Vertical gradient on the nav bar instead of a flat fill. CAUTION: this turns the bar into a colour RANGE rather than a value, so every contrast figure measured against it becomes a range too. The inverse gate measures against chrome/800 flat. */
  --fen-gradient-skeleton:           linear-gradient(90deg, var(--fen-primitive-neutral-100) 25%, var(--fen-primitive-neutral-200) 37%, var(--fen-primitive-neutral-100) 63%);  /* Loading placeholder. Pair with background-size:400% and animate background-position. */

  /* ─── SIZING ─── */
  --fen-size-nav-height:             56px;
  --fen-size-card-min:               240px;  /* Minimum card tile width before the grid reflows. */
  --fen-size-container-max:          1120px;
  --fen-size-control:                40px;  /* Button and input height. */
  --fen-size-card-art:               96px;  /* Card image height in a tile. */

  /* ─── TEXT STYLES — nine, each with a distinct job ─── */
  --fen-text-display: 700 32px/1.15 -apple-system, BlinkMacSystemFont, Segoe UI, system-ui, sans-serif;
  --fen-track-display: -2%;
  --fen-text-heading: 700 24px/1.15 -apple-system, BlinkMacSystemFont, Segoe UI, system-ui, sans-serif;
  --fen-track-heading: -1.5%;
  --fen-text-subheading: 500 20px/1.35 -apple-system, BlinkMacSystemFont, Segoe UI, system-ui, sans-serif;
  --fen-track-subheading: -1.5%;
  --fen-text-body: 400 16px/1.5 -apple-system, BlinkMacSystemFont, Segoe UI, system-ui, sans-serif;
  --fen-track-body: 0%;
  --fen-para-body: 16px;
  --fen-text-body-strong: 500 16px/1.5 -apple-system, BlinkMacSystemFont, Segoe UI, system-ui, sans-serif;
  --fen-track-body-strong: 0%;
  --fen-text-body-sm: 400 14px/1.5 -apple-system, BlinkMacSystemFont, Segoe UI, system-ui, sans-serif;
  --fen-track-body-sm: 0%;
  --fen-para-body-sm: 8px;
  --fen-text-action: 500 14px/1.5 -apple-system, BlinkMacSystemFont, Segoe UI, system-ui, sans-serif;
  --fen-track-action: 1%;
  --fen-text-label: 500 12px/1.35 -apple-system, BlinkMacSystemFont, Segoe UI, system-ui, sans-serif;
  --fen-track-label: 8%;
  --fen-case-label: uppercase;
  --fen-para-label: 0px;
  --fen-text-legal: 400 11px/1.35 -apple-system, BlinkMacSystemFont, Segoe UI, system-ui, sans-serif;
  --fen-track-legal: 0%;
  --fen-para-legal: 0px;
}

/* ═══════════════════════════════════════════════════════════
   INVERSE — a surface, not a theme.

   Not wired to prefers-color-scheme on purpose. The nav bar is
   dark because it is chrome, not because someone's OS is dark,
   and both surfaces are live on the same screen at once.

   Everything below is an ALIAS. Components keep referencing
   --fen-color-text-primary and get the right value for
   whichever surface they landed on.
   ═══════════════════════════════════════════════════════════ */
/* The bar, the sub-bar, the panel and the footer ARE chrome by
   definition — there is no light variant of them — so they carry the
   scope themselves. This is also the fix for the lime-rainbow panel:
   before the inverse existed, text-secondary resolved to a DARK
   neutral on a dark panel, so the only legible colour left to reach
   for was chartreuse. Five headers in the accent was a symptom. */
.fen-inverse,
.fen-nav,
.fen-subnav,
.fen-panel,
.fen-footer{
  --fen-inverse-surface: #25231D;
  --fen-inverse-surface-deep: #1A1814;
  --fen-inverse-surface-raised: #3A352C;
  --fen-inverse-text-primary: #FFFFFF;
  --fen-inverse-text-secondary: #C9C5BA;
  --fen-inverse-text-muted: #A19D91;
  --fen-inverse-text-brand: #3FAB51;
  --fen-inverse-border-default: #3A352C;
  --fen-inverse-border-strong: #837D6D;
  --fen-inverse-interactive: #95C1C6;
  --fen-inverse-interactive-hover: #ECF3F4;
  --fen-inverse-selected-fill: #3FAB51;
  --fen-inverse-text-on-selected: #1A1814;
  --fen-inverse-edge: #1D7732;

  /* the aliases — this is what makes components portable */
  --fen-color-surface-page: var(--fen-inverse-surface);
  --fen-color-surface-sunken: var(--fen-inverse-surface-deep);
  --fen-color-surface-raised: var(--fen-inverse-surface-raised);
  --fen-color-text-primary: var(--fen-inverse-text-primary);
  --fen-color-text-secondary: var(--fen-inverse-text-secondary);
  --fen-color-text-muted: var(--fen-inverse-text-muted);
  --fen-color-text-brand: var(--fen-inverse-text-brand);
  --fen-color-border-default: var(--fen-inverse-border-default);
  --fen-color-border-strong: var(--fen-inverse-border-strong);
  --fen-color-interactive-default: var(--fen-inverse-interactive);
  --fen-color-interactive-hover: var(--fen-inverse-interactive-hover);
  --fen-color-text-on-chrome: var(--fen-inverse-text-primary);  /* deprecated alias */
  /* border composites have to be rebuilt — they baked in the
     light border colour at generation time */
  --fen-border-default: var(--fen-borderWidth-hairline) solid var(--fen-inverse-border-default);
  --fen-border-strong: var(--fen-borderWidth-hairline) solid var(--fen-inverse-border-strong);
  --fen-border-focus: var(--fen-borderWidth-thick) solid var(--fen-inverse-text-brand);
  color: var(--fen-inverse-text-primary);
  background: var(--fen-inverse-surface);
}
/* the deeper plate — panels and footers sitting on the bar */
.fen-inverse-deep{ background: var(--fen-inverse-surface-deep) }
