Bitácora 8–30 jun: contrato firmado, plan de actividades y skill proposal-pdf
- Comunicaciones (REGISTRO #15–#21): respuesta v1.1 (10-jun), luz verde (16-jun), contrato (envío 25-jun, ajustes + firma 26-jun), arranque con Erika y kickoff 1-jul - Evidencia en fuentes/ (correos 8/10/16-jun, WhatsApp Paola y Erika) - Contratos (sin firmar y firmado) en propuesta/ - Plan de actividades Etapa 0–3 + guion del kickoff (planeacion/) - Skill proposal-pdf instalado (.claude/skills) + Propuesta-Balam.pdf regenerada - README/PENDIENTES al día; limpieza de archivos sueltos; .gitignore (locks de Office) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"_comment": "Copy to pdf.config.json next to your HTML and edit. Relative paths resolve against THIS file. Run a dry validation with: python3 scripts/build_pdf.py pdf.config.json --check",
|
||||
|
||||
"input_html": "proposal.html",
|
||||
"output_pdf": "Proposal.pdf",
|
||||
|
||||
"_comment_page": "page_size: 'Letter' | 'Legal' | 'A4' | 'A3', or a custom object {\"width_mm\":210,\"height_mm\":297}. For A4 also set @page size:A4 in the HTML CSS.",
|
||||
"page_size": "Letter",
|
||||
"cover_full_bleed": true,
|
||||
"render_timeout_ms": 30000,
|
||||
|
||||
"metadata": {
|
||||
"title": "Commercial Proposal — Project Title",
|
||||
"author": "Your Name",
|
||||
"subject": "One-line description of the proposal",
|
||||
"keywords": "proposal, optional"
|
||||
},
|
||||
|
||||
"footer": {
|
||||
"_comment": "Stamped on body pages. skip_first_page hides it on the cover; skip_pages is extra 1-based pages to skip (e.g. the TOC). page_number_format supports {page} and {pages}, e.g. '{page}' or '{page} / {pages}' or 'Pagina {page}'.",
|
||||
"text": "Commercial Proposal · Project Title · Client — Confidential",
|
||||
"skip_first_page": true,
|
||||
"skip_pages": [],
|
||||
"page_number_format": "{page}",
|
||||
"rule": true,
|
||||
"font_size_pt": 7.5,
|
||||
"margin_mm": 12,
|
||||
"color": [0.46, 0.51, 0.57]
|
||||
},
|
||||
|
||||
"fonts": {
|
||||
"_comment": "TTF for the footer text. Falls back to Helvetica if the path is missing. Debian/Ubuntu: fonts-crosextra-carlito installs this path.",
|
||||
"footer_ttf": "/usr/share/fonts/truetype/crosextra/Carlito-Regular.ttf"
|
||||
},
|
||||
|
||||
"toc": {
|
||||
"_comment": "key -> a UNIQUE phrase from that section's BODY (NOT its title; titles also render in the TOC and match there first). Avoid the first letter of a drop-cap paragraph. Match the keys to the {{PG_*}} tokens in the HTML. Leave this empty {} for a document with no table of contents.",
|
||||
"execsum": "opening paragraph uses a drop cap",
|
||||
"1": "A lede paragraph introduces the section",
|
||||
"2": "Body text for the scope section",
|
||||
"anexoA": "Appendices start on a fresh page"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,366 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
template.html — editorial proposal/report design system.
|
||||
|
||||
HOW TO USE
|
||||
• Replace the example content with yours. Keep the component markup/classes.
|
||||
• Every section that appears in the TOC gets a {{PG_key}} token in the TOC row
|
||||
AND a matching entry in pdf.config.json -> "toc" whose value is a UNIQUE
|
||||
phrase from that section's BODY (never the title — titles also render in the
|
||||
TOC and would match there first). build_pdf.py fills the tokens automatically.
|
||||
• The first page is a full-bleed cover thanks to @page:first { margin:0 }.
|
||||
• Footer (confidential line + page number) is stamped by build_pdf.py, not here.
|
||||
|
||||
COMPONENT CHEAT-SHEET (classes you can reuse)
|
||||
.cover .eyebrow/.rule/h1/.client/.meta-grid → cover page
|
||||
.toc + .toc-row(.section) → table of contents
|
||||
.sec + .sec-head/.sec-num/.kick → numbered section header
|
||||
.sec-head.no-num → header with no big number
|
||||
h3 > span.sn → sub-section heading (e.g. 2.1)
|
||||
table / th / td.k / td.num / td.cov / .ctr → tables (key cell, numeric, coverage)
|
||||
tr.total / tr.sub-total → emphasized table rows
|
||||
table.compact → tighter table for dense data
|
||||
.callout / .callout.cool → accent / navy info panels
|
||||
.flow + .chip(.accent) → pipeline / step diagram
|
||||
.synthesis + .stat → headline stat cards
|
||||
p.lede → opening paragraph
|
||||
p.drop → paragraph with drop cap
|
||||
p.mini-label → small uppercase label
|
||||
.etapa + .etapa-h → phase/stage block
|
||||
.signoff → closing signature
|
||||
.cover .logo (.txt | img.invert) → optional logo on the cover
|
||||
.break → start the block on a new page
|
||||
.keep / .no-break → keep a block from splitting across pages
|
||||
-->
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="author" content="Your Name">
|
||||
<title>Proposal Title</title>
|
||||
<style>
|
||||
/* ===========================================================================
|
||||
DESIGN FONTS — embedded, so output does NOT depend on system-installed fonts.
|
||||
The .ttf files ship in the skill at assets/fonts/. When you copy this template
|
||||
to your working file, copy that fonts/ folder next to your HTML too (so these
|
||||
url("fonts/…") paths resolve). Without it the PDF silently falls back to
|
||||
Cambria/Calibri and won't match the intended look — build_pdf.py warns when
|
||||
that happens. You don't need to edit this block.
|
||||
=========================================================================== */
|
||||
@font-face{ font-family:"Caladea"; font-style:normal; font-weight:400; src:url("fonts/Caladea-Regular.ttf") format("truetype"); }
|
||||
@font-face{ font-family:"Caladea"; font-style:normal; font-weight:700; src:url("fonts/Caladea-Bold.ttf") format("truetype"); }
|
||||
@font-face{ font-family:"Caladea"; font-style:italic; font-weight:400; src:url("fonts/Caladea-Italic.ttf") format("truetype"); }
|
||||
@font-face{ font-family:"Caladea"; font-style:italic; font-weight:700; src:url("fonts/Caladea-BoldItalic.ttf") format("truetype"); }
|
||||
@font-face{ font-family:"Carlito"; font-style:normal; font-weight:400; src:url("fonts/Carlito-Regular.ttf") format("truetype"); }
|
||||
@font-face{ font-family:"Carlito"; font-style:normal; font-weight:700; src:url("fonts/Carlito-Bold.ttf") format("truetype"); }
|
||||
@font-face{ font-family:"Carlito"; font-style:italic; font-weight:400; src:url("fonts/Carlito-Italic.ttf") format("truetype"); }
|
||||
@font-face{ font-family:"Carlito"; font-style:italic; font-weight:700; src:url("fonts/Carlito-BoldItalic.ttf") format("truetype"); }
|
||||
@font-face{ font-family:"DejaVu Sans Mono"; font-style:normal; font-weight:400; src:url("fonts/DejaVuSansMono.ttf") format("truetype"); }
|
||||
|
||||
:root{
|
||||
--ink:#1C2B39; /* deep navy — primary text + cover */
|
||||
--ink-soft:#33424F; /* secondary text */
|
||||
--muted:#6B7682; /* captions, footers */
|
||||
--accent:#BC5B3E; /* terracotta — the single accent */
|
||||
--accent-d:#9E4A30; /* darker terracotta for emphasis on tint */
|
||||
--accent-tint:#F6E9E3; /* faint terracotta fill */
|
||||
--cream:#F4EFEA; /* warm panel fill */
|
||||
--line:#D9DEE3; /* hairlines */
|
||||
--serif:"Caladea", Georgia, "Times New Roman", serif;
|
||||
--sans:"Carlito", "Helvetica Neue", Arial, sans-serif;
|
||||
--mono:"DejaVu Sans Mono", "SFMono-Regular", Consolas, monospace;
|
||||
}
|
||||
|
||||
/* For A4: change size to A4 here AND set "page_size":"A4" in pdf.config.json. */
|
||||
@page{ size:Letter; margin:15mm 16mm 18mm 16mm; }
|
||||
@page :first{ margin:0; } /* full-bleed cover */
|
||||
|
||||
*{ box-sizing:border-box; }
|
||||
html,body{ margin:0; padding:0; }
|
||||
body{
|
||||
font-family:var(--sans); color:var(--ink-soft);
|
||||
font-size:10pt; line-height:1.5;
|
||||
-webkit-print-color-adjust:exact; print-color-adjust:exact;
|
||||
}
|
||||
p{ margin:0 0 7pt; }
|
||||
strong,b{ color:var(--ink); font-weight:700; }
|
||||
em{ font-style:italic; }
|
||||
code{
|
||||
font-family:var(--mono); font-size:8.6pt;
|
||||
background:var(--cream); padding:.5pt 3pt; border-radius:2pt; color:var(--ink);
|
||||
}
|
||||
a{ color:var(--accent-d); text-decoration:none; }
|
||||
|
||||
/* ---------- COVER ---------- */
|
||||
.cover{
|
||||
width:216mm; min-height:279mm; background:var(--ink); color:#EAEDF0;
|
||||
padding:30mm 26mm 24mm; display:flex; flex-direction:column;
|
||||
position:relative; overflow:hidden;
|
||||
}
|
||||
.cover::after{ /* faint geometric corner motif */
|
||||
content:""; position:absolute; right:-60mm; top:-60mm;
|
||||
width:150mm; height:150mm; border-radius:50%;
|
||||
background:radial-gradient(circle at center, rgba(188,91,62,.18), rgba(188,91,62,0) 70%);
|
||||
}
|
||||
.cover .top,.cover .mid,.cover .meta{ position:relative; z-index:2; }
|
||||
.cover .rule{ width:46pt; height:3pt; background:var(--accent); margin-bottom:14pt; }
|
||||
.cover .eyebrow{
|
||||
font-size:9pt; letter-spacing:.32em; text-transform:uppercase; color:#9FB0BF; font-weight:700;
|
||||
}
|
||||
/* Optional logo slot (top of cover). Use a text mark or an <img>.
|
||||
.invert flips a dark logo to white for the navy cover. */
|
||||
.cover .logo{ margin-bottom:16pt; }
|
||||
.cover .logo .txt{ font-family:var(--serif); font-weight:700; font-size:14pt; color:#fff; letter-spacing:.01em; }
|
||||
.cover .logo img{ height:30pt; width:auto; }
|
||||
.cover .logo img.invert{ filter:brightness(0) invert(1); opacity:.92; }
|
||||
.cover .mid{ margin-top:auto; margin-bottom:auto; padding:18mm 0; }
|
||||
.cover h1{
|
||||
font-family:var(--serif); font-weight:700; font-size:37pt; line-height:1.06;
|
||||
letter-spacing:-.01em; margin:0; color:#FFFFFF; max-width:150mm;
|
||||
}
|
||||
.cover .client{ margin-top:16pt; font-family:var(--sans); font-size:12.5pt; color:#C9D1D8; }
|
||||
.cover .client b{ color:var(--accent); font-weight:700; }
|
||||
.cover .confidential{
|
||||
margin-top:18pt; display:flex; justify-content:space-between;
|
||||
font-size:8pt; letter-spacing:.18em; text-transform:uppercase; color:#7E8C99;
|
||||
}
|
||||
.meta-grid{ display:grid; grid-template-columns:34mm 1fr; gap:6pt 10pt; margin:0; font-size:9.2pt; }
|
||||
.meta-grid dt{ color:#8A98A5; text-transform:uppercase; letter-spacing:.12em; font-size:7.6pt; padding-top:1.5pt; }
|
||||
.meta-grid dd{ margin:0; color:#D7DDE2; }
|
||||
.meta-grid dd .who{ display:block; }
|
||||
.meta-grid dd .who b{ color:#FFFFFF; font-weight:700; }
|
||||
.meta-grid dd .who span{ color:#9FB0BF; }
|
||||
|
||||
/* ---------- TOC ---------- */
|
||||
.toc{ break-before:page; padding-top:6mm; }
|
||||
.kick{ font-size:8.5pt; letter-spacing:.28em; text-transform:uppercase; color:var(--accent); font-weight:700; }
|
||||
.toc h2{ font-family:var(--serif); font-size:26pt; font-weight:700; color:var(--ink); margin:2pt 0 4pt; }
|
||||
.toc .bar{ width:40pt; height:3pt; background:var(--accent); margin:6pt 0 16pt; }
|
||||
.toc-row{ display:flex; align-items:baseline; gap:8pt; padding:7pt 0; border-bottom:.6pt solid var(--line); font-size:10.5pt; }
|
||||
.toc-row .n{ width:22pt; color:var(--accent); font-weight:700; font-variant-numeric:tabular-nums; }
|
||||
.toc-row .t{ color:var(--ink); }
|
||||
.toc-row.section .t{ font-weight:700; }
|
||||
.toc-row .dots{ flex:1; border-bottom:1pt dotted var(--line); transform:translateY(-3pt); }
|
||||
.toc-row .pg{ color:var(--muted); font-variant-numeric:tabular-nums; }
|
||||
|
||||
/* ---------- SECTIONS ---------- */
|
||||
.content{ break-before:page; }
|
||||
.sec{ margin-bottom:14pt; }
|
||||
.sec-head{ display:flex; gap:12pt; align-items:flex-start; border-bottom:1.4pt solid var(--ink); padding-bottom:6pt; margin:0 0 11pt; }
|
||||
.sec-head .sec-num{ font-family:var(--serif); font-size:30pt; font-weight:700; line-height:.9; color:var(--accent); min-width:42pt; }
|
||||
.sec-head h2{ font-family:var(--serif); font-size:19pt; font-weight:700; color:var(--ink); margin:4pt 0 0; }
|
||||
.sec-head .kick{ display:block; margin-bottom:2pt; }
|
||||
.sec-head.no-num{ border-bottom-width:1.4pt; }
|
||||
.sec-head.no-num h2{ margin-top:0; }
|
||||
|
||||
h3{ font-family:var(--sans); font-size:11.5pt; font-weight:700; color:var(--ink); margin:13pt 0 5pt; }
|
||||
h3 .sn{ color:var(--accent); font-weight:700; margin-right:7pt; }
|
||||
h4{ font-family:var(--sans); font-size:9.5pt; font-weight:700; color:var(--ink-soft); text-transform:uppercase; letter-spacing:.06em; margin:10pt 0 3pt; }
|
||||
|
||||
p.lede{ font-size:11pt; color:var(--ink-soft); }
|
||||
p.drop::first-letter{
|
||||
font-family:var(--serif); font-size:34pt; font-weight:700; color:var(--accent);
|
||||
float:left; line-height:.82; padding:2pt 6pt 0 0;
|
||||
}
|
||||
p.mini-label{ font-size:8pt; letter-spacing:.12em; text-transform:uppercase; color:var(--accent-d); font-weight:700; margin:9pt 0 1pt; }
|
||||
|
||||
ul,ol{ margin:4pt 0 8pt; padding-left:16pt; }
|
||||
li{ margin:0 0 3.5pt; padding-left:2pt; }
|
||||
li::marker{ color:var(--accent); }
|
||||
|
||||
/* ---------- TABLES ---------- */
|
||||
table{ width:100%; border-collapse:collapse; margin:10pt 0; font-size:9.2pt; }
|
||||
thead th{
|
||||
text-align:left; font-size:7.8pt; letter-spacing:.07em; text-transform:uppercase;
|
||||
color:#FFFFFF; background:var(--ink); padding:5pt 8pt; font-weight:700;
|
||||
}
|
||||
thead th.num,thead th.ctr{ text-align:right; }
|
||||
thead th.ctr{ text-align:center; }
|
||||
tbody td{ padding:5.5pt 8pt; border-bottom:.6pt solid var(--line); vertical-align:top; }
|
||||
tbody tr:nth-child(even) td{ background:#FBFAF8; }
|
||||
td.k{ color:var(--ink); font-weight:600; }
|
||||
.num,th.num{ text-align:right; font-variant-numeric:tabular-nums; white-space:nowrap; }
|
||||
tr.total td{ font-weight:700; color:var(--ink); background:var(--cream); border-top:1.2pt solid var(--ink); border-bottom:1.2pt solid var(--ink); }
|
||||
tr.sub-total td{ font-weight:700; color:var(--ink); background:var(--accent-tint); border-top:1pt solid var(--accent); }
|
||||
td.cov{ font-size:11pt; text-align:center; line-height:1; }
|
||||
table.compact tbody td{ padding:4pt 8pt; }
|
||||
table.compact{ font-size:8.8pt; }
|
||||
|
||||
/* ---------- CALLOUTS ---------- */
|
||||
.callout{
|
||||
background:var(--accent-tint); border-left:3pt solid var(--accent);
|
||||
padding:9pt 12pt; margin:10pt 0; font-size:9.4pt; color:var(--ink-soft); border-radius:0 3pt 3pt 0;
|
||||
}
|
||||
.callout strong,.callout b{ color:var(--accent-d); }
|
||||
.callout.cool{ background:var(--cream); border-left-color:var(--ink); }
|
||||
.callout.cool strong,.callout.cool b{ color:var(--ink); }
|
||||
|
||||
/* ---------- FLOW / PIPELINE ---------- */
|
||||
.flow{ display:flex; flex-wrap:wrap; align-items:center; gap:5pt; margin:10pt 0; }
|
||||
.flow .chip{
|
||||
background:#fff; border:1pt solid var(--line); border-radius:4pt;
|
||||
padding:4pt 9pt; font-size:8.6pt; font-weight:600; color:var(--ink-soft); white-space:nowrap;
|
||||
}
|
||||
.flow .chip.accent{ background:var(--accent); border-color:var(--accent); color:#fff; }
|
||||
.flow .arrow{ color:var(--accent); font-weight:700; }
|
||||
|
||||
/* ---------- SYNTHESIS STATS ---------- */
|
||||
.synthesis{ display:flex; gap:10pt; margin:12pt 0; }
|
||||
.synthesis .stat{ flex:1; background:var(--ink); color:#EAEDF0; border-radius:5pt; padding:11pt 13pt; }
|
||||
.synthesis .stat .big{ font-family:var(--serif); font-size:21pt; font-weight:700; color:#fff; line-height:1; }
|
||||
.synthesis .stat .lab{ font-size:8pt; letter-spacing:.08em; text-transform:uppercase; color:#9FB0BF; margin-top:4pt; }
|
||||
|
||||
/* ---------- PHASE / STAGE ---------- */
|
||||
.etapa{ border:1pt solid var(--line); border-left:3pt solid var(--accent); border-radius:0 4pt 4pt 0; padding:9pt 12pt; margin:8pt 0; }
|
||||
.etapa-h{ font-weight:700; color:var(--ink); font-size:10pt; margin-bottom:3pt; }
|
||||
.etapa-h .tag{ float:right; font-size:8pt; color:var(--muted); font-weight:600; }
|
||||
|
||||
/* ---------- SIGNOFF ---------- */
|
||||
.signoff{ margin-top:18pt; padding-top:10pt; border-top:1.4pt solid var(--ink); }
|
||||
.signoff .nm{ font-family:var(--serif); font-size:14pt; font-weight:700; color:var(--ink); }
|
||||
.signoff .rl{ font-size:8.5pt; color:var(--muted); letter-spacing:.04em; }
|
||||
|
||||
/* ---------- PRINT SAFETY (keep these) ---------- */
|
||||
/* Don't strand a heading at the bottom of a page, just above its body. */
|
||||
h2,h3,h4,.sec-head{ break-after:avoid; }
|
||||
/* Repeat table header rows when a long table spans a page break, and never
|
||||
split an individual row, callout, card, phase block or the sign-off. */
|
||||
thead{ display:table-header-group; }
|
||||
tfoot{ display:table-footer-group; }
|
||||
tr{ break-inside:avoid; }
|
||||
.callout,.synthesis,.synthesis .stat,.etapa,.flow,.signoff,figure,img{ break-inside:avoid; }
|
||||
/* Utilities: .keep keeps a block together; .break starts a new page. */
|
||||
.keep,.no-break{ break-inside:avoid; }
|
||||
.break{ break-before:page; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- ============================= COVER ============================= -->
|
||||
<section class="cover">
|
||||
<div class="top">
|
||||
<!-- Optional logo: <div class="logo"><span class="txt">YOUR MARK</span></div>
|
||||
or <div class="logo"><img class="invert" src="logo.png" alt="Logo"></div>
|
||||
(use file:// abs path or a path next to the HTML; .invert whitens a dark logo) -->
|
||||
<div class="rule"></div>
|
||||
<div class="eyebrow">Commercial Proposal</div>
|
||||
</div>
|
||||
|
||||
<div class="mid">
|
||||
<h1>Project Title<br>Goes Here</h1>
|
||||
<div class="client"><b>Client Name</b></div>
|
||||
</div>
|
||||
|
||||
<div class="meta">
|
||||
<dl class="meta-grid">
|
||||
<dt>Prepared by</dt>
|
||||
<dd>Your Name — Role · City</dd>
|
||||
<dt>Date</dt>
|
||||
<dd>1 January 2026</dd>
|
||||
<dt>Version</dt>
|
||||
<dd>1.0</dd>
|
||||
<dt>Valid for</dt>
|
||||
<dd>30 calendar days from issue</dd>
|
||||
</dl>
|
||||
<div class="confidential">
|
||||
<span>Confidential document</span>
|
||||
<span>Valid 30 days</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ============================= TOC ============================= -->
|
||||
<!-- One .toc-row per section. The {{PG_key}} token is resolved by build_pdf.py. -->
|
||||
<section class="toc">
|
||||
<div class="kick">Contents</div>
|
||||
<h2>Index</h2>
|
||||
<div class="bar"></div>
|
||||
|
||||
<div class="toc-row"><span class="n">—</span><span class="t">Executive summary</span><span class="dots"></span><span class="pg">{{PG_execsum}}</span></div>
|
||||
<div class="toc-row section"><span class="n">01</span><span class="t">Understanding</span><span class="dots"></span><span class="pg">{{PG_1}}</span></div>
|
||||
<div class="toc-row section"><span class="n">02</span><span class="t">Scope</span><span class="dots"></span><span class="pg">{{PG_2}}</span></div>
|
||||
<div class="toc-row section"><span class="n">A</span><span class="t">Appendix A — Traceability</span><span class="dots"></span><span class="pg">{{PG_anexoA}}</span></div>
|
||||
</section>
|
||||
|
||||
<!-- ============================= BODY ============================= -->
|
||||
<main class="content">
|
||||
|
||||
<!-- ===== Executive summary: header with NO big number + drop cap ===== -->
|
||||
<section class="sec">
|
||||
<div class="sec-head no-num"><div><h2>Executive summary</h2></div></div>
|
||||
<p class="drop">This opening paragraph uses a drop cap. Keep the executive summary tight: the problem, the proposed first step, and the outcome. The first sentence here doubles as a good TOC anchor — pick a distinctive phrase and put it in the config.</p>
|
||||
|
||||
<div class="synthesis">
|
||||
<div class="stat"><div class="big">6–7</div><div class="lab">weeks</div></div>
|
||||
<div class="stat"><div class="big">$XX K</div><div class="lab">investment + tax</div></div>
|
||||
<div class="stat"><div class="big">4</div><div class="lab">milestones</div></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ===== Section 1: numbered header + flow diagram + callout ===== -->
|
||||
<section class="sec">
|
||||
<div class="sec-head"><div class="sec-num">01</div><div><h2>Understanding</h2></div></div>
|
||||
<p class="lede">A lede paragraph introduces the section in a slightly larger size. Distinctive opening text makes a reliable TOC anchor.</p>
|
||||
|
||||
<h3><span class="sn">1.1</span>The pipeline</h3>
|
||||
<div class="flow">
|
||||
<span class="chip">Source</span><span class="arrow">→</span>
|
||||
<span class="chip">Transform</span><span class="arrow">→</span>
|
||||
<span class="chip accent">Platform</span><span class="arrow">→</span>
|
||||
<span class="chip">Output</span>
|
||||
</div>
|
||||
|
||||
<div class="callout"><strong>Key risk to validate.</strong> Use accent callouts for the one or two things the reader must not miss. Use the <code>.cool</code> variant for neutral notes.</div>
|
||||
</section>
|
||||
|
||||
<!-- ===== Section 2: tables (stack + total row) + phase blocks ===== -->
|
||||
<section class="sec">
|
||||
<div class="sec-head"><div class="sec-num">02</div><div><h2>Scope</h2></div></div>
|
||||
<p>Body text for the scope section, with a distinctive opening phrase for the anchor.</p>
|
||||
|
||||
<table>
|
||||
<thead><tr><th>Layer</th><th>Technology</th><th class="num">Version</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td class="k">Backend</td><td>.NET</td><td class="num">10</td></tr>
|
||||
<tr><td class="k">Frontend</td><td>Angular</td><td class="num">21</td></tr>
|
||||
<tr><td class="k">Database</td><td>PostgreSQL</td><td class="num">16</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="etapa"><div class="etapa-h">Stage 0 — Discovery<span class="tag">~1 week</span></div>Short description of the stage and its deliverable.</div>
|
||||
<div class="etapa"><div class="etapa-h">Stage 1 — Core<span class="tag">~2 weeks</span></div>Short description of the stage and its deliverable.</div>
|
||||
|
||||
<table>
|
||||
<thead><tr><th>Stage</th><th class="num">Hours</th><th class="num">Investment (MXN)</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td class="k">Discovery</td><td class="num">18 – 22</td><td class="num">$10,800 – $13,200</td></tr>
|
||||
<tr><td class="k">Core</td><td class="num">32 – 39</td><td class="num">$19,200 – $23,400</td></tr>
|
||||
<tr class="total"><td>Total</td><td class="num">50 – 61 h</td><td class="num">$30,000 – $36,600</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<!-- ===== Appendix A: annex header + coverage table (emoji column) ===== -->
|
||||
<section class="sec break">
|
||||
<div class="sec-head"><div class="sec-num">A</div><div><span class="kick">Appendix</span><h2>Traceability</h2></div></div>
|
||||
<p class="lede">Appendices start on a fresh page via the <code>.break</code> class. Distinctive opening phrase for the anchor here too.</p>
|
||||
|
||||
<div class="callout cool"><strong>Legend:</strong> ✅ Covered · ⚠️ Partial · ❌ Deferred</div>
|
||||
|
||||
<table class="compact">
|
||||
<thead><tr><th style="width:46pt;">ID</th><th>Requirement</th><th class="ctr" style="width:62pt;">Coverage</th><th>Notes</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td class="k">RF-01</td><td>Example requirement</td><td class="cov">✅</td><td>Included</td></tr>
|
||||
<tr><td class="k">RF-02</td><td>Another requirement</td><td class="cov">⚠️</td><td>Partial in MVP</td></tr>
|
||||
<tr><td class="k">RF-03</td><td>A deferred requirement</td><td class="cov">❌</td><td>Later phase</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="signoff">
|
||||
<div class="nm">Your Name</div>
|
||||
<div class="rl">Role · City</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"_comment": "Smoke test: builds the template itself so you can verify the skill works end-to-end. Run: python3 scripts/build_pdf.py assets/test.config.json",
|
||||
"input_html": "template.html",
|
||||
"output_pdf": "test_out.pdf",
|
||||
"metadata": { "title": "Test Proposal", "author": "Test Author", "subject": "Smoke test" },
|
||||
"footer": { "text": "Commercial Proposal · Test · Client — Confidential", "skip_first_page": true },
|
||||
"fonts": { "footer_ttf": "/usr/share/fonts/truetype/crosextra/Carlito-Regular.ttf" },
|
||||
"toc": {
|
||||
"_comment": "anchors must be unique BODY phrases, never section titles",
|
||||
"execsum": "opening paragraph uses a drop cap",
|
||||
"1": "A lede paragraph introduces the section",
|
||||
"2": "Body text for the scope section",
|
||||
"anexoA": "Appendices start on a fresh page"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user