import { defineRoles } from "../../fonts/roles"; import { scriptWordBounds, scriptWordDecor } from "../../decor/ScriptWord"; import { FOOTER_RESERVE } from "./constants"; import { catalogEntry } from "../catalog"; import type { TemplateDefinition } from "../contract"; import { slotFor } from "../slots.optin"; import { NeutralLayoutFor } from "./layoutFor"; import { MONOGRAM_ROLES } from "./monogram"; import type { NeutralRoles, NeutralVariant } from "./variant"; const INK = "#3D3B3A"; /** The paper colour measured from Canva's texture (its average); the texture itself is stock and is not bundled. */ const PAPER = "#ECE8E1"; /** Serenity shares Monogram's Open Sans type system (docs/templates/06-serenity.md); its own object so it can drift. */ export const SERENITY_ROLES = defineRoles({ ...MONOGRAM_ROLES, } satisfies NeutralRoles); /** Letter keeps the band's top and takes this share of the page-height deficit off its position (docs: shift up about 32 pt). */ const BAND_SHIFT_SHARE = 0.64; /** * Canva A4 numbers: the rules start on the 162.91 pt left margin (the band occupies x 55-155), the * stamp slot is 82 x 82 at y 131.75 on the right. */ export const SERENITY_VARIANT: NeutralVariant = { id: "serenity", roles: SERENITY_ROLES, ink: INK, margins: { top: 56, right: 64.1, bottom: 60, left: 162.91 }, // The left margin never goes under the band's right edge plus 8 pt, whatever the preset. marginFloor: { top: 36, right: 36, bottom: 36, left: 162.91 }, ruleInset: 0, padX: 8.9, textInset: { left: 8.9, right: 6.7 }, rule: 0.75, topGap: { nominal: 75.75, min: 40 }, fromGap: 12, bandToInfo: 156.85, infoMinGap: 14, tableGap: { nominal: 61, min: 28 }, headTail: { before: 3.47, after: 6.98 }, padY: 5.8, closeGap: 7.9, subtotalPad: 8.06, totalsRowGap: 5.1, labelGap: 8, hsnMode: "subline", cols: { hsn: 0, rate: 90, qty: 36, amount: 80, descMin: 60 }, totalsW: 210, amountW: 90, wordsPlacement: "below", notesGap: { nominal: 14, min: 8 }, fromW: 250, payTo: { where: "closing", label: "Bank details", inset: 10.9 }, signatureW: 150, payGap: 0, chromeInset: 8.9, footerLeftW: 130, signatureImageH: 44, }; export const serenity: TemplateDefinition = { ...catalogEntry("serenity"), version: 1, roles: SERENITY_ROLES, margins: SERENITY_VARIANT.margins, marginFloor: SERENITY_VARIANT.marginFloor, decor: { ink: INK, background: PAPER, everyPage: (frame) => scriptWordDecor(INK, frame.heightDeficit * BAND_SHIFT_SHARE), keepOuts: (frame) => [scriptWordBounds(frame.heightDeficit * BAND_SHIFT_SHARE)], }, chrome: { color: INK, rule: null, role: SERENITY_ROLES.footer, inset: { left: SERENITY_VARIANT.chromeInset, right: SERENITY_VARIANT.chromeInset }, reserve: FOOTER_RESERVE, leftWidth: SERENITY_VARIANT.footerLeftW, }, logo: { box: { w: 82, h: 81.75 }, stackBelowTextWidth: 0, defaultOn: true, slotWidth: () => 82, slot: slotFor("serenity"), available: () => ({ w: 82, h: 81.75 }), }, Layout: NeutralLayoutFor(SERENITY_VARIANT), };