{"id":19,"date":"2026-09-04T09:04:00","date_gmt":"2026-09-04T09:04:00","guid":{"rendered":"https:\/\/preview.deltatheme.com\/kiln\/about\/"},"modified":"2026-10-21T15:17:01","modified_gmt":"2026-10-21T15:17:01","slug":"about","status":"publish","type":"page","link":"https:\/\/preview.deltatheme.com\/kiln\/about\/","title":{"rendered":"About"},"content":{"rendered":"\n  <section class=\"section\">\n    <header class=\"sechead\" data-delta-component-opt-out=\"section-head\">\n      <p class=\"sechead__eyebrow eyebrow\">About<\/p>\n      <h1 class=\"sechead__title\">A small practice.<\/h1>\n      <p class=\"sechead__lead\">Kiln has been run out of Tallinn since 2019. We take on around nine engagements a year, which is about as much as seven people can do carefully.<\/p>\n    <\/header>\n\n    <figure class=\"pagefig\">\n      <img src=\"https:\/\/preview.deltatheme.com\/kiln\/wp-content\/uploads\/sites\/2\/2026\/09\/kiln-c-response-team.jpg\" alt=\"Collage of three figures at desks in headsets during an incident\" width=\"1200\" height=\"800\">\n    <\/figure>\n  <\/section>\n\n  <ul class=\"stats\" data-delta-component=\"stat-bar\">\n    <li class=\"stats__cell\">\n      <span class=\"stats__figure\">2019<\/span>\n      <span class=\"stats__label\">Founded, Tallinn<\/span>\n    <\/li>\n    <li class=\"stats__cell\">\n      <span class=\"stats__figure\">9<\/span>\n      <span class=\"stats__label\">Engagements a year<\/span>\n    <\/li>\n    <li class=\"stats__cell\">\n      <span class=\"stats__figure\">19<\/span>\n      <span class=\"stats__label\">Advisories published<\/span>\n    <\/li>\n    <li class=\"stats__cell\">\n      <span class=\"stats__figure\">31%<\/span>\n      <span class=\"stats__label\">Open at retest<\/span>\n    <\/li>\n  <\/ul>\n\n  <section class=\"section\">\n    <header class=\"sechead\" data-delta-component=\"section-head\">\n      <p class=\"sechead__eyebrow eyebrow\">What each side gets<\/p>\n      <h2 class=\"sechead__title\">Three ways to read a report.<\/h2>\n      <p class=\"sechead__lead\">The same engagement, written up for three different readers, so nobody has to translate for anyone else.<\/p>\n    <\/header>\n\n    <div class=\"tabs\" data-delta-component-opt-out=\"tabs\">\n      <div class=\"tabs__list\" role=\"tablist\">\n        <button class=\"tabs__tab\" type=\"button\" role=\"tab\" id=\"tab-sec\" aria-controls=\"panel-sec\" aria-selected=\"true\">Security team<\/button>\n        <button class=\"tabs__tab\" type=\"button\" role=\"tab\" id=\"tab-eng\" aria-controls=\"panel-eng\" aria-selected=\"false\">Engineering<\/button>\n        <button class=\"tabs__tab\" type=\"button\" role=\"tab\" id=\"tab-lead\" aria-controls=\"panel-lead\" aria-selected=\"false\">Leadership<\/button>\n      <\/div>\n\n      <div class=\"tabs__panel\" role=\"tabpanel\" id=\"panel-sec\" aria-labelledby=\"tab-sec\">\n        <div class=\"pane\">\n        <figure class=\"quote quote--testimonial pane__quote\" data-delta-component=\"blockquote\">\n          <blockquote class=\"quote__body\">\n            <p>They handed us the timeline before they handed us the report. We had detections written for two of the routes while the operation was still running.<\/p>\n          <\/blockquote>\n          <figcaption class=\"quote__by\">\n            <span class=\"quote__who\">Head of security operations<\/span>\n            <span class=\"quote__role\">Nordic payments platform<\/span>\n          <\/figcaption>\n        <\/figure>\n        <div class=\"pane__copy\">\n        <h3 class=\"pane__title\">The narrative, not the score.<\/h3>\n        <p class=\"pane__body\">The operation written down in order and timestamped against your own logs, including the routes that dead-ended. Those are often the more useful half.<\/p>\n        <ul class=\"checks\" data-delta-component=\"checklist\">\n          <li class=\"checks__item\">\n            <span class=\"checks__ico\"><span class=\"delta-icon\" data-delta-icon-pack=\"tabler-outline\" data-delta-icon=\"check\" aria-hidden=\"true\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n  <path d=\"M5 12l5 5l10 -10\"\/>\n<\/svg><\/span><\/span>\n            <p class=\"checks__text\">Full attack narrative, in order, with timestamps you can match against your logs.<\/p>\n          <\/li>\n          <li class=\"checks__item\">\n            <span class=\"checks__ico\"><span class=\"delta-icon\" data-delta-icon-pack=\"tabler-outline\" data-delta-icon=\"check\" aria-hidden=\"true\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n  <path d=\"M5 12l5 5l10 -10\"\/>\n<\/svg><\/span><\/span>\n            <p class=\"checks__text\">Detection rules for every technique we used, and whether they fired.<\/p>\n          <\/li>\n          <li class=\"checks__item\">\n            <span class=\"checks__ico\"><span class=\"delta-icon\" data-delta-icon-pack=\"tabler-outline\" data-delta-icon=\"check\" aria-hidden=\"true\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n  <path d=\"M5 12l5 5l10 -10\"\/>\n<\/svg><\/span><\/span>\n            <p class=\"checks__text\">The routes that failed, which is usually the more useful half.<\/p>\n          <\/li>\n        <\/ul>\n        <\/div>\n        <\/div>\n      <\/div>\n\n      <div class=\"tabs__panel\" role=\"tabpanel\" id=\"panel-eng\" aria-labelledby=\"tab-eng\">\n        <div class=\"pane pane--flip\">\n        <figure class=\"quote quote--testimonial pane__quote\" data-delta-component=\"blockquote\">\n          <blockquote class=\"quote__body\">\n            <p>The first finding arrived as a pull request. Four vendors before them sent a PDF and an invoice, and we did the translation ourselves both times.<\/p>\n          <\/blockquote>\n          <figcaption class=\"quote__by\">\n            <span class=\"quote__who\">Principal engineer<\/span>\n            <span class=\"quote__role\">Logistics platform, Rotterdam<\/span>\n          <\/figcaption>\n        <\/figure>\n        <div class=\"pane__copy\">\n        <h3 class=\"pane__title\">Something to reproduce.<\/h3>\n        <p class=\"pane__body\">Every finding comes with steps that reproduce it and, where the fix is ours to write, a patch. We also describe the pattern behind it, so the repair is not limited to one place.<\/p>\n        <ul class=\"checks\" data-delta-component=\"checklist\">\n          <li class=\"checks__item\">\n            <span class=\"checks__ico\"><span class=\"delta-icon\" data-delta-icon-pack=\"tabler-outline\" data-delta-icon=\"check\" aria-hidden=\"true\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n  <path d=\"M5 12l5 5l10 -10\"\/>\n<\/svg><\/span><\/span>\n            <p class=\"checks__text\">A working proof for every finding, not a screenshot of a scanner.<\/p>\n          <\/li>\n          <li class=\"checks__item\">\n            <span class=\"checks__ico\"><span class=\"delta-icon\" data-delta-icon-pack=\"tabler-outline\" data-delta-icon=\"check\" aria-hidden=\"true\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n  <path d=\"M5 12l5 5l10 -10\"\/>\n<\/svg><\/span><\/span>\n            <p class=\"checks__text\">A patch that compiles, or a pull request, where the fix is ours to write.<\/p>\n          <\/li>\n          <li class=\"checks__item\">\n            <span class=\"checks__ico\"><span class=\"delta-icon\" data-delta-icon-pack=\"tabler-outline\" data-delta-icon=\"check\" aria-hidden=\"true\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n  <path d=\"M5 12l5 5l10 -10\"\/>\n<\/svg><\/span><\/span>\n            <p class=\"checks__text\">The pattern behind the finding, so you fix four places rather than one.<\/p>\n          <\/li>\n        <\/ul>\n        <\/div>\n        <\/div>\n      <\/div>\n\n      <div class=\"tabs__panel\" role=\"tabpanel\" id=\"panel-lead\" aria-labelledby=\"tab-lead\">\n        <div class=\"pane\">\n        <figure class=\"quote quote--testimonial pane__quote\" data-delta-component=\"blockquote\">\n          <blockquote class=\"quote__body\">\n            <p>One page, and I knew what to do with it. The ninety-day retest is now the first thing I ask any vendor whether they will commit to.<\/p>\n          <\/blockquote>\n          <figcaption class=\"quote__by\">\n            <span class=\"quote__who\">Chief technology officer<\/span>\n            <span class=\"quote__role\">Insurance group, Munich<\/span>\n          <\/figcaption>\n        <\/figure>\n        <div class=\"pane__copy\">\n        <h3 class=\"pane__title\">One page, plainly written.<\/h3>\n        <p class=\"pane__body\">What was reachable, what that would have meant, and a short note ninety days later saying whether the fixes held.<\/p>\n        <ul class=\"checks\" data-delta-component=\"checklist\">\n          <li class=\"checks__item\">\n            <span class=\"checks__ico\"><span class=\"delta-icon\" data-delta-icon-pack=\"tabler-outline\" data-delta-icon=\"check\" aria-hidden=\"true\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n  <path d=\"M5 12l5 5l10 -10\"\/>\n<\/svg><\/span><\/span>\n            <p class=\"checks__text\">One page, no jargon, written for a board rather than a SOC.<\/p>\n          <\/li>\n          <li class=\"checks__item\">\n            <span class=\"checks__ico\"><span class=\"delta-icon\" data-delta-icon-pack=\"tabler-outline\" data-delta-icon=\"check\" aria-hidden=\"true\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n  <path d=\"M5 12l5 5l10 -10\"\/>\n<\/svg><\/span><\/span>\n            <p class=\"checks__text\">Risk in business terms: what was reachable, and what it would have cost.<\/p>\n          <\/li>\n          <li class=\"checks__item\">\n            <span class=\"checks__ico\"><span class=\"delta-icon\" data-delta-icon-pack=\"tabler-outline\" data-delta-icon=\"check\" aria-hidden=\"true\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n  <path d=\"M5 12l5 5l10 -10\"\/>\n<\/svg><\/span><\/span>\n            <p class=\"checks__text\">Written confirmation, ninety days later, that the fixes held.<\/p>\n          <\/li>\n        <\/ul>\n        <\/div>\n        <\/div>\n      <\/div>\n    <\/div>\n  <\/section>\n\n  <section class=\"section inverted\">\n    <header class=\"sechead\" data-delta-component=\"section-head\">\n      <p class=\"sechead__eyebrow eyebrow\">Method<\/p>\n      <h2 class=\"sechead__title\">How an engagement runs.<\/h2>\n      <p class=\"sechead__lead\">The shape is fixed so you can plan around it. What happens inside each block is not, and it varies by estate.<\/p>\n    <\/header>\n\n    <ul class=\"cells cells--2\">\n      <li class=\"step\" data-delta-component=\"step-card\">\n        <p class=\"prompt\"><span class=\"prompt__host\">kiln@engagement:~$<\/span> scope<\/p>\n        <p class=\"step__meta\">Two weeks<\/p>\n        <p class=\"step__body\">We map what you actually run rather than what the asset register says. The gap between the two is usually where the work is.<\/p>\n      <\/li>\n      <li class=\"step\" data-delta-component=\"step-card\">\n        <p class=\"prompt\"><span class=\"prompt__host\">kiln@engagement:~$<\/span> breach<\/p>\n        <p class=\"step__meta\">Four weeks<\/p>\n        <p class=\"step__body\">Live operation against production, under rules you write. You get a channel we post to daily rather than a monthly summary.<\/p>\n      <\/li>\n      <li class=\"step\" data-delta-component=\"step-card\">\n        <p class=\"prompt\"><span class=\"prompt__host\">kiln@engagement:~$<\/span> report<\/p>\n        <p class=\"step__meta\">One week<\/p>\n        <p class=\"step__body\">Findings ordered by what they lead to rather than by score. Each one has steps to reproduce it and a suggested fix.<\/p>\n      <\/li>\n      <li class=\"step\" data-delta-component=\"step-card\">\n        <p class=\"prompt\"><span class=\"prompt__host\">kiln@engagement:~$<\/span> retest<\/p>\n        <p class=\"step__meta\">Ongoing<\/p>\n        <p class=\"step__body\">We come back and try the same routes ninety days later, and record what held and what did not.<\/p>\n      <\/li>\n    <\/ul>\n  <\/section>\n\n  <section class=\"section\">\n    <header class=\"sechead\" data-delta-component=\"section-head\">\n      <p class=\"sechead__eyebrow eyebrow\">The team<\/p>\n      <h2 class=\"sechead__title\">Who you would be working with.<\/h2>\n      <p class=\"sechead__lead\">Four of us are below. Three work under client agreements that do not allow a public profile.<\/p>\n    <\/header>\n\n    <ul class=\"profiles\">\n      <li class=\"person person--profile\" data-delta-component=\"person\">\n        <figure class=\"person__fig\">\n          <img src=\"https:\/\/preview.deltatheme.com\/kiln\/wp-content\/uploads\/sites\/2\/2026\/09\/kiln-eleni-sarris.jpg\" alt=\"Eleni Sarris\" width=\"600\" height=\"600\">\n        <\/figure>\n        <div class=\"person__body\">\n          <p class=\"person__role eyebrow\">Principal<\/p>\n          <h3 class=\"person__name\">Eleni Sarris<\/h3>\n          <p class=\"person__line\">Founded Kiln after eleven years of red team work for two banks and a shipping group. Writes the rules of engagement for each job and leads the longer ones.<\/p>\n          <ul class=\"badges\">\n            <li class=\"badge\" data-delta-component=\"badge\">Adversary simulation<\/li>\n          <\/ul>\n          <a class=\"person__mail\" href=\"mailto:eleni@kiln.example\">eleni@kiln.example<\/a>\n        <\/div>\n      <\/li>\n\n      <li class=\"person person--profile\" data-delta-component=\"person\">\n        <figure class=\"person__fig\">\n          <img src=\"https:\/\/preview.deltatheme.com\/kiln\/wp-content\/uploads\/sites\/2\/2026\/09\/kiln-marcus-bramwell.jpg\" alt=\"Marcus Bramwell\" width=\"600\" height=\"600\">\n        <\/figure>\n        <div class=\"person__body\">\n          <p class=\"person__role eyebrow\">Head of research<\/p>\n          <h3 class=\"person__name\">Marcus Bramwell<\/h3>\n          <p class=\"person__line\">Most of our published advisories came out of his research. Spends roughly a third of the year looking at software nobody asked him to look at.<\/p>\n          <ul class=\"badges\">\n            <li class=\"badge\" data-delta-component=\"badge\">Vulnerability research<\/li>\n          <\/ul>\n          <a class=\"person__mail\" href=\"mailto:marcus@kiln.example\">marcus@kiln.example<\/a>\n        <\/div>\n      <\/li>\n\n      <li class=\"person person--profile\" data-delta-component=\"person\">\n        <figure class=\"person__fig\">\n          <img src=\"https:\/\/preview.deltatheme.com\/kiln\/wp-content\/uploads\/sites\/2\/2026\/09\/kiln-nadia-okonjo.jpg\" alt=\"Nadia Okonjo\" width=\"600\" height=\"600\">\n        <\/figure>\n        <div class=\"person__body\">\n          <p class=\"person__role eyebrow\">Cloud and identity lead<\/p>\n          <h3 class=\"person__name\">Nadia Okonjo<\/h3>\n          <p class=\"person__line\">Came from platform engineering rather than security, which is why her findings arrive as pull requests. Argues that identity is where the boundaries actually are.<\/p>\n          <ul class=\"badges\">\n            <li class=\"badge\" data-delta-component=\"badge\">AWS and Azure<\/li>\n          <\/ul>\n          <a class=\"person__mail\" href=\"mailto:nadia@kiln.example\">nadia@kiln.example<\/a>\n        <\/div>\n      <\/li>\n\n      <li class=\"person person--profile\" data-delta-component=\"person\">\n        <figure class=\"person__fig\">\n          <img src=\"https:\/\/preview.deltatheme.com\/kiln\/wp-content\/uploads\/sites\/2\/2026\/09\/kiln-tomas-reinholt.jpg\" alt=\"Tomas Reinholt\" width=\"600\" height=\"600\">\n        <\/figure>\n        <div class=\"person__body\">\n          <p class=\"person__role eyebrow\">Detection engineering<\/p>\n          <h3 class=\"person__name\">Tomas Reinholt<\/h3>\n          <p class=\"person__line\">Sits on the defensive side of an engagement. Turns what the team did into detection rules, then tries the same routes again to see whether they fire.<\/p>\n          <ul class=\"badges\">\n            <li class=\"badge\" data-delta-component=\"badge\">Detection rules<\/li>\n          <\/ul>\n          <a class=\"person__mail\" href=\"mailto:tomas@kiln.example\">tomas@kiln.example<\/a>\n        <\/div>\n      <\/li>\n    <\/ul>\n\n    <div class=\"secfoot\">\n      <a class=\"secfoot__link\" href=\"https:\/\/preview.deltatheme.com\/kiln\/book-a-call\/\">Arrange a call<\/a>\n    <\/div>\n  <\/section>\n","protected":false},"excerpt":{"rendered":"<p>About A small practice. Kiln has been run out of Tallinn since 2019. We take on around nine engagements a year, which is about as much as seven people can do carefully. 2019 Founded, Tallinn 9 Engagements a year 19 Advisories published 31% Open at retest What each side gets Three ways to read a report. The same engagement, written up for three different readers, so nobody has to translate for anyone else. Security team Engineering Leadership They handed us the timeline before they handed us the<\/p>\n","protected":false},"author":2,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-19","page","type-page","status-publish","hentry"],"delta":{"html":"\n  <section class=\"section\">\n    <header class=\"sechead\" data-delta-component-opt-out=\"section-head\">\n      <p class=\"sechead__eyebrow eyebrow\">About<\/p>\n      <h1 class=\"sechead__title\">A small practice.<\/h1>\n      <p class=\"sechead__lead\">Kiln has been run out of Tallinn since 2019. We take on around nine engagements a year, which is about as much as seven people can do carefully.<\/p>\n    <\/header>\n\n    <figure class=\"pagefig\">\n      <img src=\"https:\/\/preview.deltatheme.com\/kiln\/wp-content\/uploads\/sites\/2\/2026\/09\/kiln-c-response-team.jpg\" alt=\"Collage of three figures at desks in headsets during an incident\" width=\"1200\" height=\"800\">\n    <\/figure>\n  <\/section>\n\n  <ul class=\"stats\" data-delta-component=\"stat-bar\">\n    <li class=\"stats__cell\">\n      <span class=\"stats__figure\">2019<\/span>\n      <span class=\"stats__label\">Founded, Tallinn<\/span>\n    <\/li>\n    <li class=\"stats__cell\">\n      <span class=\"stats__figure\">9<\/span>\n      <span class=\"stats__label\">Engagements a year<\/span>\n    <\/li>\n    <li class=\"stats__cell\">\n      <span class=\"stats__figure\">19<\/span>\n      <span class=\"stats__label\">Advisories published<\/span>\n    <\/li>\n    <li class=\"stats__cell\">\n      <span class=\"stats__figure\">31%<\/span>\n      <span class=\"stats__label\">Open at retest<\/span>\n    <\/li>\n  <\/ul>\n\n  <section class=\"section\">\n    <header class=\"sechead\" data-delta-component=\"section-head\">\n      <p class=\"sechead__eyebrow eyebrow\">What each side gets<\/p>\n      <h2 class=\"sechead__title\">Three ways to read a report.<\/h2>\n      <p class=\"sechead__lead\">The same engagement, written up for three different readers, so nobody has to translate for anyone else.<\/p>\n    <\/header>\n\n    <div class=\"tabs\" data-delta-component-opt-out=\"tabs\">\n      <div class=\"tabs__list\" role=\"tablist\">\n        <button class=\"tabs__tab\" type=\"button\" role=\"tab\" id=\"tab-sec\" aria-controls=\"panel-sec\" aria-selected=\"true\">Security team<\/button>\n        <button class=\"tabs__tab\" type=\"button\" role=\"tab\" id=\"tab-eng\" aria-controls=\"panel-eng\" aria-selected=\"false\">Engineering<\/button>\n        <button class=\"tabs__tab\" type=\"button\" role=\"tab\" id=\"tab-lead\" aria-controls=\"panel-lead\" aria-selected=\"false\">Leadership<\/button>\n      <\/div>\n\n      <div class=\"tabs__panel\" role=\"tabpanel\" id=\"panel-sec\" aria-labelledby=\"tab-sec\">\n        <div class=\"pane\">\n        <figure class=\"quote quote--testimonial pane__quote\" data-delta-component=\"blockquote\">\n          <blockquote class=\"quote__body\">\n            <p>They handed us the timeline before they handed us the report. We had detections written for two of the routes while the operation was still running.<\/p>\n          <\/blockquote>\n          <figcaption class=\"quote__by\">\n            <span class=\"quote__who\">Head of security operations<\/span>\n            <span class=\"quote__role\">Nordic payments platform<\/span>\n          <\/figcaption>\n        <\/figure>\n        <div class=\"pane__copy\">\n        <h3 class=\"pane__title\">The narrative, not the score.<\/h3>\n        <p class=\"pane__body\">The operation written down in order and timestamped against your own logs, including the routes that dead-ended. Those are often the more useful half.<\/p>\n        <ul class=\"checks\" data-delta-component=\"checklist\">\n          <li class=\"checks__item\">\n            <span class=\"checks__ico\"><span class=\"delta-icon\" data-delta-icon-pack=\"tabler-outline\" data-delta-icon=\"check\" aria-hidden=\"true\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n  <path d=\"M5 12l5 5l10 -10\"\/>\n<\/svg><\/span><\/span>\n            <p class=\"checks__text\">Full attack narrative, in order, with timestamps you can match against your logs.<\/p>\n          <\/li>\n          <li class=\"checks__item\">\n            <span class=\"checks__ico\"><span class=\"delta-icon\" data-delta-icon-pack=\"tabler-outline\" data-delta-icon=\"check\" aria-hidden=\"true\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n  <path d=\"M5 12l5 5l10 -10\"\/>\n<\/svg><\/span><\/span>\n            <p class=\"checks__text\">Detection rules for every technique we used, and whether they fired.<\/p>\n          <\/li>\n          <li class=\"checks__item\">\n            <span class=\"checks__ico\"><span class=\"delta-icon\" data-delta-icon-pack=\"tabler-outline\" data-delta-icon=\"check\" aria-hidden=\"true\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n  <path d=\"M5 12l5 5l10 -10\"\/>\n<\/svg><\/span><\/span>\n            <p class=\"checks__text\">The routes that failed, which is usually the more useful half.<\/p>\n          <\/li>\n        <\/ul>\n        <\/div>\n        <\/div>\n      <\/div>\n\n      <div class=\"tabs__panel\" role=\"tabpanel\" id=\"panel-eng\" aria-labelledby=\"tab-eng\">\n        <div class=\"pane pane--flip\">\n        <figure class=\"quote quote--testimonial pane__quote\" data-delta-component=\"blockquote\">\n          <blockquote class=\"quote__body\">\n            <p>The first finding arrived as a pull request. Four vendors before them sent a PDF and an invoice, and we did the translation ourselves both times.<\/p>\n          <\/blockquote>\n          <figcaption class=\"quote__by\">\n            <span class=\"quote__who\">Principal engineer<\/span>\n            <span class=\"quote__role\">Logistics platform, Rotterdam<\/span>\n          <\/figcaption>\n        <\/figure>\n        <div class=\"pane__copy\">\n        <h3 class=\"pane__title\">Something to reproduce.<\/h3>\n        <p class=\"pane__body\">Every finding comes with steps that reproduce it and, where the fix is ours to write, a patch. We also describe the pattern behind it, so the repair is not limited to one place.<\/p>\n        <ul class=\"checks\" data-delta-component=\"checklist\">\n          <li class=\"checks__item\">\n            <span class=\"checks__ico\"><span class=\"delta-icon\" data-delta-icon-pack=\"tabler-outline\" data-delta-icon=\"check\" aria-hidden=\"true\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n  <path d=\"M5 12l5 5l10 -10\"\/>\n<\/svg><\/span><\/span>\n            <p class=\"checks__text\">A working proof for every finding, not a screenshot of a scanner.<\/p>\n          <\/li>\n          <li class=\"checks__item\">\n            <span class=\"checks__ico\"><span class=\"delta-icon\" data-delta-icon-pack=\"tabler-outline\" data-delta-icon=\"check\" aria-hidden=\"true\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n  <path d=\"M5 12l5 5l10 -10\"\/>\n<\/svg><\/span><\/span>\n            <p class=\"checks__text\">A patch that compiles, or a pull request, where the fix is ours to write.<\/p>\n          <\/li>\n          <li class=\"checks__item\">\n            <span class=\"checks__ico\"><span class=\"delta-icon\" data-delta-icon-pack=\"tabler-outline\" data-delta-icon=\"check\" aria-hidden=\"true\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n  <path d=\"M5 12l5 5l10 -10\"\/>\n<\/svg><\/span><\/span>\n            <p class=\"checks__text\">The pattern behind the finding, so you fix four places rather than one.<\/p>\n          <\/li>\n        <\/ul>\n        <\/div>\n        <\/div>\n      <\/div>\n\n      <div class=\"tabs__panel\" role=\"tabpanel\" id=\"panel-lead\" aria-labelledby=\"tab-lead\">\n        <div class=\"pane\">\n        <figure class=\"quote quote--testimonial pane__quote\" data-delta-component=\"blockquote\">\n          <blockquote class=\"quote__body\">\n            <p>One page, and I knew what to do with it. The ninety-day retest is now the first thing I ask any vendor whether they will commit to.<\/p>\n          <\/blockquote>\n          <figcaption class=\"quote__by\">\n            <span class=\"quote__who\">Chief technology officer<\/span>\n            <span class=\"quote__role\">Insurance group, Munich<\/span>\n          <\/figcaption>\n        <\/figure>\n        <div class=\"pane__copy\">\n        <h3 class=\"pane__title\">One page, plainly written.<\/h3>\n        <p class=\"pane__body\">What was reachable, what that would have meant, and a short note ninety days later saying whether the fixes held.<\/p>\n        <ul class=\"checks\" data-delta-component=\"checklist\">\n          <li class=\"checks__item\">\n            <span class=\"checks__ico\"><span class=\"delta-icon\" data-delta-icon-pack=\"tabler-outline\" data-delta-icon=\"check\" aria-hidden=\"true\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n  <path d=\"M5 12l5 5l10 -10\"\/>\n<\/svg><\/span><\/span>\n            <p class=\"checks__text\">One page, no jargon, written for a board rather than a SOC.<\/p>\n          <\/li>\n          <li class=\"checks__item\">\n            <span class=\"checks__ico\"><span class=\"delta-icon\" data-delta-icon-pack=\"tabler-outline\" data-delta-icon=\"check\" aria-hidden=\"true\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n  <path d=\"M5 12l5 5l10 -10\"\/>\n<\/svg><\/span><\/span>\n            <p class=\"checks__text\">Risk in business terms: what was reachable, and what it would have cost.<\/p>\n          <\/li>\n          <li class=\"checks__item\">\n            <span class=\"checks__ico\"><span class=\"delta-icon\" data-delta-icon-pack=\"tabler-outline\" data-delta-icon=\"check\" aria-hidden=\"true\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n  <path d=\"M5 12l5 5l10 -10\"\/>\n<\/svg><\/span><\/span>\n            <p class=\"checks__text\">Written confirmation, ninety days later, that the fixes held.<\/p>\n          <\/li>\n        <\/ul>\n        <\/div>\n        <\/div>\n      <\/div>\n    <\/div>\n  <\/section>\n\n  <section class=\"section inverted\">\n    <header class=\"sechead\" data-delta-component=\"section-head\">\n      <p class=\"sechead__eyebrow eyebrow\">Method<\/p>\n      <h2 class=\"sechead__title\">How an engagement runs.<\/h2>\n      <p class=\"sechead__lead\">The shape is fixed so you can plan around it. What happens inside each block is not, and it varies by estate.<\/p>\n    <\/header>\n\n    <ul class=\"cells cells--2\">\n      <li class=\"step\" data-delta-component=\"step-card\">\n        <p class=\"prompt\"><span class=\"prompt__host\">kiln@engagement:~$<\/span> scope<\/p>\n        <p class=\"step__meta\">Two weeks<\/p>\n        <p class=\"step__body\">We map what you actually run rather than what the asset register says. The gap between the two is usually where the work is.<\/p>\n      <\/li>\n      <li class=\"step\" data-delta-component=\"step-card\">\n        <p class=\"prompt\"><span class=\"prompt__host\">kiln@engagement:~$<\/span> breach<\/p>\n        <p class=\"step__meta\">Four weeks<\/p>\n        <p class=\"step__body\">Live operation against production, under rules you write. You get a channel we post to daily rather than a monthly summary.<\/p>\n      <\/li>\n      <li class=\"step\" data-delta-component=\"step-card\">\n        <p class=\"prompt\"><span class=\"prompt__host\">kiln@engagement:~$<\/span> report<\/p>\n        <p class=\"step__meta\">One week<\/p>\n        <p class=\"step__body\">Findings ordered by what they lead to rather than by score. Each one has steps to reproduce it and a suggested fix.<\/p>\n      <\/li>\n      <li class=\"step\" data-delta-component=\"step-card\">\n        <p class=\"prompt\"><span class=\"prompt__host\">kiln@engagement:~$<\/span> retest<\/p>\n        <p class=\"step__meta\">Ongoing<\/p>\n        <p class=\"step__body\">We come back and try the same routes ninety days later, and record what held and what did not.<\/p>\n      <\/li>\n    <\/ul>\n  <\/section>\n\n  <section class=\"section\">\n    <header class=\"sechead\" data-delta-component=\"section-head\">\n      <p class=\"sechead__eyebrow eyebrow\">The team<\/p>\n      <h2 class=\"sechead__title\">Who you would be working with.<\/h2>\n      <p class=\"sechead__lead\">Four of us are below. Three work under client agreements that do not allow a public profile.<\/p>\n    <\/header>\n\n    <ul class=\"profiles\">\n      <li class=\"person person--profile\" data-delta-component=\"person\">\n        <figure class=\"person__fig\">\n          <img src=\"https:\/\/preview.deltatheme.com\/kiln\/wp-content\/uploads\/sites\/2\/2026\/09\/kiln-eleni-sarris.jpg\" alt=\"Eleni Sarris\" width=\"600\" height=\"600\">\n        <\/figure>\n        <div class=\"person__body\">\n          <p class=\"person__role eyebrow\">Principal<\/p>\n          <h3 class=\"person__name\">Eleni Sarris<\/h3>\n          <p class=\"person__line\">Founded Kiln after eleven years of red team work for two banks and a shipping group. Writes the rules of engagement for each job and leads the longer ones.<\/p>\n          <ul class=\"badges\">\n            <li class=\"badge\" data-delta-component=\"badge\">Adversary simulation<\/li>\n          <\/ul>\n          <a class=\"person__mail\" href=\"mailto:eleni@kiln.example\">eleni@kiln.example<\/a>\n        <\/div>\n      <\/li>\n\n      <li class=\"person person--profile\" data-delta-component=\"person\">\n        <figure class=\"person__fig\">\n          <img src=\"https:\/\/preview.deltatheme.com\/kiln\/wp-content\/uploads\/sites\/2\/2026\/09\/kiln-marcus-bramwell.jpg\" alt=\"Marcus Bramwell\" width=\"600\" height=\"600\">\n        <\/figure>\n        <div class=\"person__body\">\n          <p class=\"person__role eyebrow\">Head of research<\/p>\n          <h3 class=\"person__name\">Marcus Bramwell<\/h3>\n          <p class=\"person__line\">Most of our published advisories came out of his research. Spends roughly a third of the year looking at software nobody asked him to look at.<\/p>\n          <ul class=\"badges\">\n            <li class=\"badge\" data-delta-component=\"badge\">Vulnerability research<\/li>\n          <\/ul>\n          <a class=\"person__mail\" href=\"mailto:marcus@kiln.example\">marcus@kiln.example<\/a>\n        <\/div>\n      <\/li>\n\n      <li class=\"person person--profile\" data-delta-component=\"person\">\n        <figure class=\"person__fig\">\n          <img src=\"https:\/\/preview.deltatheme.com\/kiln\/wp-content\/uploads\/sites\/2\/2026\/09\/kiln-nadia-okonjo.jpg\" alt=\"Nadia Okonjo\" width=\"600\" height=\"600\">\n        <\/figure>\n        <div class=\"person__body\">\n          <p class=\"person__role eyebrow\">Cloud and identity lead<\/p>\n          <h3 class=\"person__name\">Nadia Okonjo<\/h3>\n          <p class=\"person__line\">Came from platform engineering rather than security, which is why her findings arrive as pull requests. Argues that identity is where the boundaries actually are.<\/p>\n          <ul class=\"badges\">\n            <li class=\"badge\" data-delta-component=\"badge\">AWS and Azure<\/li>\n          <\/ul>\n          <a class=\"person__mail\" href=\"mailto:nadia@kiln.example\">nadia@kiln.example<\/a>\n        <\/div>\n      <\/li>\n\n      <li class=\"person person--profile\" data-delta-component=\"person\">\n        <figure class=\"person__fig\">\n          <img src=\"https:\/\/preview.deltatheme.com\/kiln\/wp-content\/uploads\/sites\/2\/2026\/09\/kiln-tomas-reinholt.jpg\" alt=\"Tomas Reinholt\" width=\"600\" height=\"600\">\n        <\/figure>\n        <div class=\"person__body\">\n          <p class=\"person__role eyebrow\">Detection engineering<\/p>\n          <h3 class=\"person__name\">Tomas Reinholt<\/h3>\n          <p class=\"person__line\">Sits on the defensive side of an engagement. Turns what the team did into detection rules, then tries the same routes again to see whether they fire.<\/p>\n          <ul class=\"badges\">\n            <li class=\"badge\" data-delta-component=\"badge\">Detection rules<\/li>\n          <\/ul>\n          <a class=\"person__mail\" href=\"mailto:tomas@kiln.example\">tomas@kiln.example<\/a>\n        <\/div>\n      <\/li>\n    <\/ul>\n\n    <div class=\"secfoot\">\n      <a class=\"secfoot__link\" href=\"https:\/\/preview.deltatheme.com\/kiln\/book-a-call\/\">Arrange a call<\/a>\n    <\/div>\n  <\/section>\n","css":"\/* About \u2014 page-scoped layout. *\/\n\n.profiles {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n  display: grid;\n  border-top: var(--rule);\n}\n\n.profiles > * {\n  border-bottom: var(--rule);\n}\n\n.profiles > :last-child {\n  border-bottom: 0;\n}\n\n@media (min-width: 64rem) {\n  .profiles {\n    grid-template-columns: repeat(2, 1fr);\n  }\n\n  .profiles > * {\n    border-right: var(--rule);\n  }\n\n  .profiles > :nth-child(2n) {\n    border-right: 0;\n  }\n\n  .profiles > :nth-last-child(-n+2) {\n    border-bottom: 0;\n  }\n}\n\/* Tab panes. Two columns from md up: a figure on one side, the copy and its\n   checklist on the other. The side alternates by tab (--flip moves the figure\n   to column two) so the three panels do not read as one repeated slab.\n\n   The panel drops its own padding when it holds a pane, because the figure runs\n   to the panel edge and the copy column carries the padding instead. *\/\n.tabs__panel:has(.pane) {\n  padding: 0;\n}\n\n.pane {\n  display: grid;\n}\n\n\/* The column holds a testimonial rather than an image. The blockquote component\n   brings its own ground, padding and top accent rule; this only centres it in a\n   column whose height is set by the copy beside it. *\/\n\/* The column holds a testimonial rather than an image. The blockquote component\n   brings its own ground and padding; this centres it in a column whose height\n   is set by the copy beside it, and drops the variant's action-coloured top\n   rule \u2014 the selected tab already draws one directly above it, and two the same\n   colour a few pixels apart read as one thick broken line. *\/\n\/* .quote.pane__quote, not .pane__quote alone: the testimonial variant sets the\n   accent rule at 0,1,0 and components.css loads after this file, so a single\n   class tied and lost. Doubling the class wins at 0,2,0. *\/\n.quote.pane__quote {\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  border-block-start: 0;\n}\n\n.pane__copy {\n  padding: var(--space-2xl) var(--pad);\n}\n\n.pane__title {\n  margin: 0 0 var(--space-sm);\n  font-family: var(--font-title);\n  font-size: var(--title-sm-size);\n  line-height: var(--title-sm-leading);\n  letter-spacing: var(--title-sm-tracking);\n  font-weight: var(--weight-semibold);\n}\n\n.pane__body {\n  margin: 0 0 var(--space-xl);\n  max-width: var(--line-length-md);\n  color: var(--color-ink-muted);\n}\n\n@media (min-width: 64rem) {\n  .pane {\n    grid-template-columns: 1fr 1fr;\n    align-items: stretch;\n  }\n\n  .pane__quote {\n    border-right: var(--rule);\n  }\n\n  .pane--flip .pane__quote {\n    grid-column: 2;\n    grid-row: 1;\n    border-right: 0;\n    border-left: var(--rule);\n  }\n\n  .pane--flip .pane__copy {\n    grid-column: 1;\n    grid-row: 1;\n  }\n}\n","js":"","styles":[{"handle":"21press-delta-tokens","url":"https:\/\/preview.deltatheme.com\/kiln\/wp-content\/uploads\/sites\/2\/21press-delta\/site\/tokens.css","inline":""},{"handle":"21press-delta-engine","url":"https:\/\/preview.deltatheme.com\/kiln\/wp-content\/themes\/delta\/assets\/frontend\/css\/base.css","inline":""},{"handle":"21press-delta-components","url":"https:\/\/preview.deltatheme.com\/kiln\/wp-content\/uploads\/sites\/2\/21press-delta\/site\/components.css","inline":"\/**\n * Site CSS \u2014 element defaults, composed properties, surface rows, context classes.\n * Delta's floor (reset, page frame, nav reset) lives in the theme's base.css.\n * Agent-authored via set-site-assets. Prefer var(--\u2026) from tokens.css.\n *\n * Colour works in rows. tokens.css holds the light row on :root. This file\n * holds the dark row and the contrast row, the same job names with other\n * values. A section switches rows with one class (surface-contrast); the\n * whole site switches with data-theme=\"dark\" on <html>. Nothing inside a\n * section changes: every element reads the same names.\n *\/\n\n:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {\n  outline: 2px solid var(--color-focus, var(--color-action));\n  outline-offset: 2px;\n}\n\nul.delta-nav ul {\n  padding-left: var(--space-md);\n}\n\nbody {\n  font-family: var(--font-text);\n  font-size: var(--text-md-size);\n  line-height: var(--text-md-leading);\n  letter-spacing: var(--text-md-tracking);\n}\n\nh1,\nh2,\nh3,\nh4 {\n  font-family: var(--font-title);\n  color: var(--color-ink);\n}\n\nh1 {\n  font-size: var(--title-xl-size);\n  line-height: var(--title-xl-leading);\n  letter-spacing: var(--title-xl-tracking);\n  font-weight: var(--weight-semibold);\n}\n\nh2 {\n  font-size: var(--title-lg-size);\n  line-height: var(--title-lg-leading);\n  letter-spacing: var(--title-lg-tracking);\n  font-weight: var(--weight-semibold);\n}\n\nh3 {\n  font-size: var(--title-md-size);\n  line-height: var(--title-md-leading);\n  letter-spacing: var(--title-md-tracking);\n  font-weight: var(--weight-semibold);\n}\n\ncode,\nkbd,\nsamp {\n  font-family: var(--font-mono);\n  font-size: var(--text-code-size);\n  line-height: var(--text-code-leading);\n}\n\npre {\n  font-family: var(--font-mono);\n  font-size: var(--text-code-size);\n  line-height: var(--text-code-leading);\n  overflow-x: auto;\n}\n\n\/* The block already took the code size; code inside it must not shrink again. *\/\npre code {\n  font-size: inherit;\n  line-height: inherit;\n}\n\n\/* Eyebrow: a kicker above a heading, set in the label step. Not a heading. *\/\n.eyebrow {\n  font-size: var(--text-label-size);\n  line-height: var(--text-label-leading);\n  letter-spacing: var(--text-label-tracking);\n  text-transform: uppercase;\n  font-weight: var(--weight-semibold);\n  color: var(--color-ink-muted);\n}\n\n\/**\n * Content rhythm. The floor zeroes browser margins; this puts space above each\n * text block, between siblings only, so the first and last never leave a gap.\n * Zero specificity (:where), so any template or recipe rule wins.\n *\/\n:where(p, ul, ol, dl, pre, blockquote, figure, table) + :where(p, ul, ol, dl, pre, blockquote, figure, table) {\n  margin-block-start: var(--space-sm);\n}\n\n:where(p) + :where(p) {\n  margin-block-start: var(--space-md);\n}\n\n:where(h1, h2, h3, h4, h5, h6) + :where(p, ul, ol, dl, pre, blockquote, figure, table) {\n  margin-block-start: var(--space-sm);\n}\n\n\/* A page title wants one step more under it than a heading in the flow. *\/\n:where(h1) + :where(p, ul, ol, dl, pre, blockquote, figure, table) {\n  margin-block-start: var(--space-md);\n}\n\n:where(p, ul, ol, dl, pre, blockquote, figure, table) + :where(h1, h2, h3, h4, h5, h6) {\n  margin-block-start: var(--space-lg);\n}\n\n:where(.eyebrow) + :where(h1, h2, h3, h4, h5, h6) {\n  margin-block-start: var(--space-xs);\n}\n\n\/* Unclassed text links only. A bare `a` \/ `a:hover` colour rule is 0,1,1 on\n   hover and paints `.btn` labels the same colour as the fill. *\/\na:not([class]) {\n  color: var(--color-ink);\n}\n\na:not([class]):hover {\n  color: var(--color-ink-muted);\n}\n\n.section__inner {\n  \/* Running text. A shorter line: calc(var(--layout-content) * 0.7) \u2014 never a raw ch. *\/\n  max-width: var(--layout-content);\n  font-size: var(--text-md-size);\n  line-height: var(--text-md-leading);\n  letter-spacing: var(--text-md-tracking);\n  color: var(--color-ink);\n}\n\n.section__inner h2,\n.section__inner h3 {\n  font-family: var(--font-title);\n  font-weight: var(--weight-medium);\n}\n\n.section__inner h2 {\n  font-size: var(--title-lg-size);\n  line-height: var(--title-lg-leading);\n  letter-spacing: var(--title-lg-tracking);\n}\n\n.section__inner h3 {\n  font-size: var(--title-md-size);\n  line-height: var(--title-md-leading);\n  letter-spacing: var(--title-md-tracking);\n}\n\n.section__inner p {\n  color: var(--color-ink-muted);\n}\n\n\/* Links inside body text only. Never a bare `.x a { color }` (0,1,1): it outranks\n   a component's own color (.btn is 0,1,0) and paints button labels invisible.\n   The link_color_scope write invariant enforces this; the seed obeys it too.\n   Hover must be restated: this selector ties a:not([class]):hover at 0,2,1. *\/\n.section__inner a:not([class]) {\n  color: var(--color-ink);\n}\n\n.section__inner a:not([class]):hover {\n  color: var(--color-ink-muted);\n}\n\n\/**\n * Surface rows.\n *\n * The light row lives in tokens.css (:root). The two rows below set the same\n * job names with other values. A component never knows which row it is in.\n *\/\n\n\/* Site-wide dark mode: data-theme=\"dark\" on <html>. Inert until something\n   sets it (the light-and-dark recipe adds a header script and a toggle).\n   Declared on :root, never on body: a value on body would override the\n   dark value body inherits from <html>. *\/\n:root[data-theme=\"dark\"] {\n  --color-surface-body: #18181B;\n  --color-surface-subtle: #1F1F22;\n  --color-surface-raised: #27272A;\n  --color-ink: #FAFAFA;\n  --color-ink-muted: #A1A1AA;\n  --color-border: #3F3F46;\n  --color-action: #FAFAFA;\n  --color-action-hover: #E4E4E7;\n  --color-action-subtle: #27272A;\n  --color-focus: #FAFAFA;\n  --color-on-action: #18181B;\n  --color-danger: #F87171;\n  --color-danger-bg: #452A2C;\n  --color-success: #4ADE80;\n  --color-success-bg: #224030;\n  --color-warning: #FBBF24;\n  --color-warning-bg: #45391D;\n  \/* Shadows need their own dark values: a light shadow on a dark ground is invisible. *\/\n  --shadow-sm: 0 1px 2px 0 rgb(0 0 0 \/ 0.4);\n  --shadow-md: 0 4px 6px -1px rgb(0 0 0 \/ 0.5), 0 2px 4px -2px rgb(0 0 0 \/ 0.5);\n  --shadow-lg: 0 10px 15px -3px rgb(0 0 0 \/ 0.6), 0 4px 6px -4px rgb(0 0 0 \/ 0.6);\n}\n\n\/* The inverted band: dark on a light page, one per page. Same values as the\n   dark row. Put the class (or the attribute) on the section wrapper and\n   nothing inside it needs to change. *\/\n.surface-contrast,\n[data-surface=\"contrast\"] {\n  --color-surface-body: #18181B;\n  --color-surface-subtle: #1F1F22;\n  --color-surface-raised: #27272A;\n  --color-ink: #FAFAFA;\n  --color-ink-muted: #A1A1AA;\n  --color-border: #3F3F46;\n  --color-action: #FAFAFA;\n  --color-action-hover: #E4E4E7;\n  --color-action-subtle: #27272A;\n  --color-focus: #FAFAFA;\n  --color-on-action: #18181B;\n  --color-danger: #F87171;\n  --color-danger-bg: #452A2C;\n  --color-success: #4ADE80;\n  --color-success-bg: #224030;\n  --color-warning: #FBBF24;\n  --color-warning-bg: #45391D;\n  \/* Shadows need their own dark values: a light shadow on a dark ground is invisible. *\/\n  --shadow-sm: 0 1px 2px 0 rgb(0 0 0 \/ 0.4);\n  --shadow-md: 0 4px 6px -1px rgb(0 0 0 \/ 0.5), 0 2px 4px -2px rgb(0 0 0 \/ 0.5);\n  --shadow-lg: 0 10px 15px -3px rgb(0 0 0 \/ 0.6), 0 4px 6px -4px rgb(0 0 0 \/ 0.6);\n}\n\n\/* The inverted band on a dark page is light: the light row again. *\/\n:root[data-theme=\"dark\"] .surface-contrast,\n:root[data-theme=\"dark\"] [data-surface=\"contrast\"] {\n  --color-surface-body: #FFFFFF;\n  --color-surface-subtle: #F4F4F5;\n  --color-surface-raised: #FFFFFF;\n  --color-ink: #09090B;\n  --color-ink-muted: #6E6E77;\n  --color-border: #E4E4E7;\n  --color-action: #18181B;\n  --color-action-hover: #27272A;\n  --color-action-subtle: #F4F4F5;\n  --color-focus: #18181B;\n  --color-on-action: #FAFAFA;\n  --color-danger: #DC2626;\n  --color-danger-bg: #FEF2F2;\n  --color-success: #16A34A;\n  --color-success-bg: #F0FDF4;\n  --color-warning: #D97706;\n  --color-warning-bg: #FFFBEB;\n  --shadow-sm: 0 1px 2px 0 rgb(0 0 0 \/ 0.05);\n  --shadow-md: 0 4px 6px -1px rgb(0 0 0 \/ 0.10), 0 2px 4px -2px rgb(0 0 0 \/ 0.10);\n  --shadow-lg: 0 10px 15px -3px rgb(0 0 0 \/ 0.10), 0 4px 6px -4px rgb(0 0 0 \/ 0.10);\n}\n\n\/* Every surface paints its own row. `color` must be set here: it inherits as\n   an already computed value, so a rebinding alone would leave the old ink. *\/\nbody,\n.surface-contrast,\n[data-surface=\"contrast\"] {\n  background: var(--color-surface-body);\n  color: var(--color-ink);\n}\n\n\/* Quiet band between sections. Same row, one step off the page. *\/\n.surface-subtle {\n  background: var(--color-surface-subtle);\n}\n\n\/* A lifted card or panel. On light the border and shadow lift it; on dark\n   the tone does. The agent writes the class and never chooses between them. *\/\n.surface-raised {\n  background: var(--color-surface-raised);\n  border: var(--border-width) solid var(--color-border);\n  box-shadow: var(--shadow-sm);\n}\n\n\/* The contrast band in this site's own colours, carried over from the names the earlier seed gave them. *\/\n.surface-contrast,\n[data-surface=\"contrast\"] {\n  --color-surface-body: var(--color-surface-contrast);\n  --color-on-action: var(--color-surface-contrast);\n  --color-ink: var(--color-ink-on-contrast);\n  --color-action: var(--color-ink-on-contrast);\n  --color-focus: var(--color-ink-on-contrast);\n  --color-ink-muted: var(--color-ink-muted-on-contrast);\n  --color-action-hover: var(--color-ink-muted-on-contrast);\n  --color-border: var(--color-border-on-contrast);\n}\n\n\/**\n * Kept from this site at the seed upgrade: its own rules, its changes to seed\n * rules, and rules of the earlier seed that this seed has no version of.\n * They come after the seed's rules, so where both speak these win.\n *\/\n\n\/**\n * Site CSS \u2014 element defaults, composed properties, context classes.\n *\/\n\n\/* ---- Composed properties -------------------------------------------\n   Site-level decisions the token file does not carry. Bare names: the\n   --color-* \/ --space-* namespace belongs to tokens.json. *\/\n\/* Declared on every element, not on :root. A custom property that references\n   another is resolved on the element that declares it, and descendants inherit\n   the finished value. On :root these two froze to the light colours, so\n   .inverted and dark mode, which rebind --color-border and --color-ink, could\n   not reach them. On * each element resolves them from its own colour tokens. *\/\n* {\n  --rule: var(--border-width) solid var(--color-border);\n  \/* A control's edge is not a layout hairline. --color-border is 1.19:1 on the\n     page ground \u2014 fine for a rule that separates blocks, and a failure on\n     anything a person operates, where WCAG 1.4.11 asks for 3:1. Form controls\n     take this token; the secondary and ghost buttons take --color-ink-muted,\n     which clears the same floor without a token of its own.\n     Named here so the intent is on the record and the two can move apart. *\/\n  --color-field-border: var(--color-ink);\n}\n\n:root {\n  --pad: var(--layout-section-inline);\n  --mono-sm: var(--text-xs-size);\n  \/* Vertical rhythm. Named for the job, not the size \u2014 --gap-md would only be a\n     second word for --space-md and would not tell anyone where to use it.\n     These five say where they belong, so a block picks the one that matches its\n     relationship and the size follows. Change the mapping here and the rhythm\n     moves everywhere at once, which is the point of naming them at all.\n\n     Vertical space is set with `gap` on the parent, not margin on the child.\n     Where a block needs two rhythms \u2014 a label hugging its heading inside a\n     looser stack \u2014 nest a container rather than reaching back for margins.\n     Two exceptions that gap cannot express: `margin-top: auto` pinning a link\n     to the bottom of a card, and the space below the last child of a block,\n     which is that block's padding. *\/\n  --gap-tight: var(--space-2xs);   \/* a label and the thing it labels *\/\n  --gap-stack: var(--space-md);    \/* siblings inside one text block *\/\n  --gap-inline: var(--space-sm);   \/* items sitting in a row *\/\n  --gap-block: var(--space-lg);    \/* distinct blocks inside a component *\/\n  --gap-group: var(--space-2xl);   \/* major groups inside a section *\/\n\n  \/* Line length. Every max-width on running text derives from --layout-content,\n     so the whole family moves when that one token moves. There is no bare ch\n     value left on the site: the twelve that outlived the first pass now sit on\n     these six steps, each within a character or two of what it replaced.\n     --line-length-title caps a section h2; --line-length-title-page caps the h1\n     in a section head, which is set one size up and needs the wider cap.\n     Resolved values at the current 65ch content measure are in the comments. *\/\n  --line-length-title: calc(var(--layout-content) * 0.28);       \/* ~18ch \u2014 section titles *\/\n  --line-length-title-page: calc(var(--layout-content) * 0.37);  \/* ~24ch \u2014 page titles *\/\n  --line-length-xs: calc(var(--layout-content) * 0.5);           \/* ~33ch \u2014 cell captions *\/\n  --line-length-sm: calc(var(--layout-content) * 0.65);          \/* ~42ch \u2014 leads, cells *\/\n  --line-length-md: calc(var(--layout-content) * 0.71);          \/* ~46ch \u2014 body in a column *\/\n  --line-length-lg: calc(var(--layout-content) * 1.05);          \/* ~68ch \u2014 long-form *\/\n\n  \/* Breakpoints are literals by necessity: a custom property cannot be read in\n     a media query condition. Every query on this site is written against the\n     token values \u2014 40rem is --breakpoint-sm, 64rem is --breakpoint-md, and\n     63.99rem is the max-width side of md. Change a breakpoint token and these\n     literals must be changed with it. --breakpoint-lg (80rem) is unused: no\n     layout on the site changes at that width. *\/\n}\n\n\/* ---- Element defaults ------------------------------------------------ *\/\n\n\/* border-box for everything. The site was written without it, so every box was\n   content-box: .site-header and .site-footer both set inline-size:100% and then\n   add padding and a side rule on top, which put them 2x--pad wider than .shell\n   at every width and overflowed the viewport below ~1498px. .field__in did the\n   same inside its column. The templates already say they are \"contained to the\n   same measure as .shell\" \u2014 this is what makes that true. *\/\n*,\n*::before,\n*::after {\n  box-sizing: border-box;\n}\n\nbody {\n  margin: 0;\n  font-family: var(--font-text);\n  font-size: var(--text-md-size);\n  line-height: var(--text-md-leading);\n  letter-spacing: var(--text-md-tracking);\n  color: var(--color-ink);\n  background: var(--color-surface-body);\n  -webkit-font-smoothing: antialiased;\n}\n\na {\n  color: var(--color-action);\n}\n\n\/* :where() keeps this at 0,0,1. A bare a:hover is 0,1,1 and outranks a\n   component's own color (.btn--primary is 0,1,0) \u2014 which paints a button label\n   in the same value the hover state paints its background. *\/\na:where(:hover) {\n  color: var(--color-action-hover);\n}\n\n:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {\n  outline: var(--space-3xs) solid var(--color-focus);\n  outline-offset: var(--space-3xs);\n}\n\ncode,\nkbd,\npre,\nsamp {\n  font-family: var(--font-mono);\n  font-size: var(--mono-sm);\n}\n\nhr {\n  border: 0;\n  border-top: var(--rule);\n  margin: var(--space-2xl) 0;\n}\n\nimg {\n  max-width: 100%;\n  height: auto;\n}\n\n\/* ---- Layout context -------------------------------------------------\n   Full-bleed section, constrained inner column. Every page inherits this\n   skeleton, so two pages built months apart still reconcile.\n\n   The page column is the theme's own main.delta-main, not a wrapper div in\n   each page. Every page and template used to open with <div class=\"shell\">,\n   which made the whole page one block: the Front panel lists the direct\n   children of the page as its Sections, so it showed one entry and could\n   reorder nothing. Pages and templates now start with their sections.\n   inline-size is needed because .delta-site is a flex column: with\n   margin-inline:auto and no width, the column shrinks to its content and the\n   widest child pushes it past the viewport. *\/\n.delta-main {\n  inline-size: 100%;\n  max-width: var(--layout-max);\n  margin-inline: auto;\n  background: var(--color-surface-body);\n  border-inline: var(--rule);\n}\n\n.delta-main > * {\n  min-width: 0;\n}\n\n.section {\n  border-bottom: var(--rule);\n}\n\n\/* The footer draws its own top rule, so the last block in the page column must not\n   draw a bottom one or the hairline doubles above the footer on every page.\n   This was written as `.shell > :has(+ .site-footer)`, which never matched: the\n   footer is a sibling of main.delta-main, not of .shell, so it is not the\n   shell's next element. Closing on :last-child needs no knowledge of what\n   follows the shell. *\/\n.delta-main > :last-child {\n  border-bottom: 0;\n}\n\n.section__inner {\n  padding: var(--layout-section-block) var(--pad);\n}\n\n.section__inner--measure {\n  max-width: var(--layout-content);\n}\n\n\/* Links inside body text only. Never a bare `.x a { color }` (0,1,1): it\n   outranks a component's own color (.btn is 0,1,0) and paints button labels\n   invisible. *\/\n.section__inner a:not([class]) {\n  color: var(--color-action);\n}\n\n\/* The one eyebrow. Every short label that sits above a heading and names what\n   follows uses this and nothing else \u2014 section heads, hero, service bands. It\n   carries its own colour and its own space below, so it works on its own; a\n   block that needs a different layout (an icon beside it, say) adds that and\n   changes nothing about the type. Do not write a second mono variant: the\n   role is the same, so the style is the same. *\/\n.eyebrow {\n  display: block;\n  margin: 0;\n  font-family: var(--font-mono);\n  font-size: var(--mono-sm);\n  line-height: var(--title-2xs-leading);\n  letter-spacing: var(--tracking-wide);\n  font-weight: var(--weight-regular);\n  text-transform: uppercase;\n  color: var(--color-action);\n}\n\n.cluster {\n  display: flex;\n  flex-wrap: wrap;\n  align-items: center;\n  gap: var(--space-sm);\n}\n\n\/* A row of buttons stacks and goes full width on a narrow screen: side by side\n   they either wrap into a ragged pair or squeeze their labels. Scoped with\n   :has(> .btn) so a cluster of badges or meta keeps flowing as a row. *\/\n@media (max-width: 39.99rem) {\n  .cluster:has(> .btn) {\n    flex-direction: column;\n    align-items: stretch;\n  }\n}\n\n\/* ---- Terminal caption -----------------------------------------------\n   A command line used as a label. It began inside the intake form, then the\n   method grid needed the same idiom, so it moved here rather than being copied\n   \u2014 the same mistake .pgrid made. The host segment carries the accent; the\n   command after it takes the surrounding ink. *\/\n.prompt {\n  margin: 0 0 var(--space-xl);\n  font-family: var(--font-mono);\n  font-size: var(--mono-sm);\n  color: var(--color-ink-muted);\n}\n\n.prompt__host {\n  color: var(--color-action);\n}\n\n\/* The action colour is 3.4:1 on the contrast ground, under the floor. Lifted\n   towards the light ink rather than replaced, so the segment still reads as an\n   accent, and derived from the tokens so it follows a palette change. *\/\n.inverted .prompt__host {\n  color: color-mix(in srgb, var(--color-action), var(--color-ink-on-contrast) 45%);\n}\n\n\/* ---- Ruled cell grid -------------------------------------------------\n   A shared layout utility, not a component: the grid owns every hairline and\n   the cells inside it draw none. Modifier picks the column count at the md\n   breakpoint. The nth-child maths is tied to that count, so changing the\n   number of columns means revisiting the border rules. *\/\n.cells {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n  display: grid;\n  border-top: var(--rule);\n}\n\n.cells > * {\n  border-bottom: var(--rule);\n}\n\n.cells > :last-child {\n  border-bottom: 0;\n}\n\n.secfoot {\n  padding: var(--space-xl) var(--pad);\n  border-top: var(--rule);\n}\n\n.secfoot__link {\n  display: inline-flex;\n  align-items: center;\n  gap: var(--space-2xs);\n  padding-bottom: var(--space-3xs);\n  font-family: var(--font-mono);\n  font-size: var(--mono-sm);\n  color: var(--color-ink);\n  text-decoration: none;\n  border-bottom: var(--border-width) solid var(--color-action);\n}\n\n.secfoot__link:hover {\n  color: var(--color-action);\n}\n\n@media (min-width: 40rem) {\n  .cells--2,\n  .cells--3,\n  .cells--4 {\n    grid-template-columns: repeat(2, 1fr);\n  }\n\n  .cells--2 > * {\n    border-right: var(--rule);\n  }\n\n  .cells--2 > :nth-child(2n) {\n    border-right: 0;\n  }\n\n  .cells--2 > :nth-last-child(-n+2) {\n    border-bottom: 0;\n  }\n\n  .cells--3 > *,\n  .cells--4 > * {\n    border-right: var(--rule);\n  }\n\n  .cells--3 > :nth-child(2n),\n  .cells--4 > :nth-child(2n) {\n    border-right: 0;\n  }\n\n  .cells--3 > :last-child,\n  .cells--4 > :last-child {\n    border-bottom: var(--rule);\n  }\n}\n\n@media (min-width: 64rem) {\n  .cells--3 {\n    grid-template-columns: repeat(3, 1fr);\n  }\n\n  .cells--3 > :nth-child(2n) {\n    border-right: var(--rule);\n  }\n\n  .cells--3 > :nth-child(3n) {\n    border-right: 0;\n  }\n\n  .cells--3 > :nth-last-child(-n+3) {\n    border-bottom: 0;\n  }\n\n  .cells--4 {\n    grid-template-columns: repeat(4, 1fr);\n  }\n\n  .cells--4 > :nth-child(2n) {\n    border-right: var(--rule);\n  }\n\n  .cells--4 > :nth-child(4n) {\n    border-right: 0;\n  }\n\n  \/* Matches the .cells--3 form above, and it has to: `.cells--4 > *` is 0,1,0\n     and loses to the `:last-child` rule at the sm breakpoint, which left the\n     fourth cell drawing a bottom rule against whatever followed the grid. *\/\n  .cells--4 > :nth-last-child(-n+4) {\n    border-bottom: 0;\n  }\n}\n\n\/* ---- Inverted context ------------------------------------------------\n   color must be declared here: it inherits from body as an already computed\n   value, so rebinding --color-ink alone leaves descendants on the light ink. *\/\n.inverted {\n  color: var(--color-ink);\n  background: var(--color-surface-contrast);\n  --color-ink: var(--color-ink-on-contrast);\n  --color-ink-muted: var(--color-ink-muted-on-contrast);\n  --color-border: var(--color-border-on-contrast);\n  --color-surface-subtle: color-mix(in srgb, var(--color-surface-contrast), white 7%);\n  \/* action, action-hover and on-action are deliberately NOT rebound: a filled\n     button carries its own ground, so it reads the same here as on a light\n     section. Only bare text links need help \u2014 the brand colour is 3.4:1 on this\n     ground, so they take the light ink instead (see the rule below). *\/\n  --color-focus: var(--color-ink-on-contrast);\n}\n\n\/* The eyebrow is action-coloured on a light ground, which is 3.4:1 on this one\n   \u2014 under the 4.5 floor. It takes the light ink here for the same reason bare\n   links below do. The icon inside it follows, because it inherits currentColor. *\/\n.inverted .eyebrow {\n  color: var(--color-ink-on-contrast);\n}\n\n\/* Unclassed links only. A classed element \u2014 a button \u2014 keeps its own colours,\n   which is what makes the filled button identical on light and inverted grounds. *\/\n.inverted a:not([class]) {\n  color: var(--color-ink-on-contrast);\n}\n\n.inverted a:not([class]):where(:hover) {\n  color: var(--color-ink-muted-on-contrast);\n}\n\n\/* ---- Ruled post grid -------------------------------------------------\n   Shared utility, not a component: used by the blog archive and by the related\n   block on a single post. It lived in the archive template CSS, which does not\n   load on a single, so the related grid rendered unstyled. The grid owns every\n   hairline and the cards inside draw none. *\/\n.pgrid {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n  display: grid;\n  border-top: var(--rule);\n}\n\n.pgrid > * {\n  border-bottom: var(--rule);\n}\n\n.pgrid > :last-child {\n  border-bottom: 0;\n}\n\n\/* The final row draws no bottom rule: the section around the grid closes it,\n   the same arrangement .cells uses. Re-adding a rule on the last cell alone\n   doubled the hairline against the section's own. The nth-last-child count is\n   tied to the column count, so changing one means changing the other. *\/\n@media (min-width: 40rem) {\n  .pgrid {\n    grid-template-columns: repeat(2, 1fr);\n  }\n\n  .pgrid > * {\n    border-right: var(--rule);\n  }\n\n  .pgrid > :nth-child(2n) {\n    border-right: 0;\n  }\n\n  .pgrid > :nth-last-child(-n+2) {\n    border-bottom: 0;\n  }\n}\n\n@media (min-width: 64rem) {\n  .pgrid {\n    grid-template-columns: repeat(3, 1fr);\n  }\n\n  .pgrid > :nth-child(2n) {\n    border-right: var(--rule);\n  }\n\n  .pgrid > :nth-child(3n) {\n    border-right: 0;\n  }\n\n  .pgrid > :nth-last-child(-n+3) {\n    border-bottom: 0;\n  }\n}\n\n\/* ---- Pager -----------------------------------------------------------\n   Shared utility, not a component. It was written three times \u2014 in the blog\n   archive, the term archive and the search results template \u2014 and the copies\n   had already drifted apart (two on --space-sm, one on --gap-inline) before\n   anyone noticed, which is the same way .pgrid went wrong. It sits here beside\n   .pgrid because every template that lists posts also pages them.\n   --gap-inline is the right name for it: these are items sitting in a row. *\/\n.pager {\n  display: flex;\n  flex-wrap: wrap;\n  align-items: center;\n  gap: var(--gap-inline);\n  padding: var(--space-xl) var(--pad);\n  border-top: var(--rule);\n  font-family: var(--font-mono);\n  font-size: var(--mono-sm);\n}\n\n\/* :empty does not match here \u2014 the pagination loop leaves whitespace behind\n   even when it renders no links, which is enough to defeat it. Test for the\n   links themselves, or an empty band with a rule above it sits under the grid. *\/\n.pager:not(:has(a)) {\n  display: none;\n}\n\n.pager__n {\n  display: inline-grid;\n  place-items: center;\n  min-inline-size: var(--space-xl);\n  padding: var(--space-2xs) var(--space-xs);\n  border: var(--rule);\n  color: var(--color-ink-muted);\n  text-decoration: none;\n}\n\n.pager__n:hover {\n  color: var(--color-action);\n  border-color: var(--color-action);\n}\n\n.pager__n[aria-current] {\n  background: var(--color-action);\n  border-color: var(--color-action);\n  color: var(--color-on-action);\n}\n\n\/* ---- Form controls ---------------------------------------------------\n   The field primitives \u2014 .field, .field__lbl, .field__in and the rest \u2014 live\n   on the `field` component, not here. They were written in both files, and\n   this copy loaded last and won, so editing the component recipe changed\n   nothing on the page and only its custom select arrow ever reached the\n   screen. Component CSS applies by class across the whole site, with no\n   data-delta-component marker needed, so the search bar below takes the same\n   controls from the component. Only what is not the control stays here. *\/\n\n\/* A single control beside its submit. Stacks below sm, where a button sitting\n   next to a full-width input leaves neither enough room. *\/\n.searchbar {\n  display: grid;\n  gap: var(--space-sm);\n  max-width: var(--line-length-lg);\n}\n\n@media (min-width: 40rem) {\n  .searchbar {\n    grid-template-columns: 1fr auto;\n    align-items: end;\n  }\n\n  .searchbar .field {\n    grid-column: 1;\n  }\n}\n\n\/* ---- Page figure -----------------------------------------------------\n   A full-bleed band under a page title. Shared: the services page and the blog\n   archive both use it, so it lives here rather than in either one.\n\n   Full-bleed means the inner edge of the page column (.delta-main), not the\n   viewport \u2014 its side rules are the edge of the design. The figure takes\n   no --pad, so it runs rule to rule.\n\n   A fixed ratio with object-fit: cover, because the source images are 16:9 and\n   the band is wider than that; an auto ratio would let the band's height follow\n   whatever image is dropped in. Shorter ratio on narrow screens, where a 16:5\n   crop leaves nothing readable in the frame. *\/\n.pagefig {\n  margin: 0;\n  aspect-ratio: 16 \/ 9;\n  overflow: hidden;\n  background: var(--color-surface-subtle);\n  border-bottom: var(--rule);\n}\n\n.pagefig img {\n  display: block;\n  inline-size: 100%;\n  block-size: 100%;\n  object-fit: cover;\n  object-position: center;\n}\n\n\/* Below md the band leads the page: the image comes first, then the title and\n   lead below it. order:-1 needs a flex context, and the section is a plain\n   block, so :has() gives one only to the sections that carry a page figure.\n   The rule moves to the block end for the same reason \u2014 the figure is now the\n   first thing under the header, not the last thing under the copy. *\/\n@media (max-width: 39.99rem) {\n  .section:has(> .pagefig) {\n    display: flex;\n    flex-direction: column;\n  }\n\n  .pagefig {\n    order: -1;\n    border-bottom: 0;\n    border-top: var(--rule);\n  }\n}\n\n@media (min-width: 40rem) {\n  .pagefig {\n    aspect-ratio: 16 \/ 5;\n  }\n}\n\n\/* ---- Closing call to action -----------------------------------------\n   Shared utility. It began in the Services page CSS; the Case study page\n   needed the same block, so it moved here rather than being copied \u2014 the\n   same mistake .pgrid and .pager made. The title cap moved from a bare 20ch\n   to --line-length-title, the lead cap from 46ch to --line-length-md. *\/\n.cta {\n  display: grid;\n  gap: var(--space-lg);\n  padding: var(--space-3xl) var(--pad);\n  align-items: center;\n}\n\n.cta__title {\n  margin: 0;\n  font-family: var(--font-title);\n  font-size: var(--title-lg-size);\n  line-height: var(--title-lg-leading);\n  letter-spacing: var(--title-lg-tracking);\n  font-weight: var(--weight-bold);\n  max-width: var(--line-length-title);\n  text-wrap: balance;\n}\n\n.cta__lead {\n  margin: var(--space-md) 0 0;\n  max-width: var(--line-length-md);\n  color: var(--color-ink-muted);\n}\n\n\/* Stacked, and both buttons the same width: min-inline-size sets the floor\n   and the wider label sets the rest, so the pair reads as one block. *\/\n.cta__actions {\n  display: flex;\n  flex-direction: column;\n  align-items: stretch;\n  gap: var(--space-sm);\n  min-inline-size: 16rem;\n}\n\n@media (min-width: 64rem) {\n  .cta {\n    grid-template-columns: 1fr auto;\n    gap: var(--space-2xl);\n  }\n}\n\n\/* Dot grid behind the call to action, on a pseudo-element so the mask fades\n   only the grid and not the section ground or its text. isolation keeps the\n   negative z-index inside this section. Dot colour is mixed from the section's\n   own ink, so a token change moves it. *\/\n.section:has(> .cta) {\n  position: relative;\n  isolation: isolate;\n}\n\n.section:has(> .cta)::before {\n  content: \"\";\n  position: absolute;\n  inset: 0;\n  z-index: -1;\n  pointer-events: none;\n  background-image: radial-gradient(circle at 1px 1px, color-mix(in srgb, var(--color-ink-on-contrast) 16%, transparent) 1px, transparent 0);\n  background-size: var(--space-xl) var(--space-xl);\n  -webkit-mask-image: radial-gradient(120% 120% at 100% 0%, black 20%, transparent 72%);\n  mask-image: radial-gradient(120% 120% at 100% 0%, black 20%, transparent 72%);\n}\n\n\/* Contained to the same measure as the page column (.delta-main), with the same side rules, so the\n   bar reads as the top of the page column rather than a band behind it. The\n   footer does the same at the other end. *\/\n.site-header {\n  position: sticky;\n  top: 0;\n  z-index: 20;\n  display: flex;\n  align-items: center;\n  gap: var(--space-md);\n  inline-size: 100%;\n  max-width: var(--layout-max);\n  margin-inline: auto;\n  padding: var(--space-sm) var(--pad);\n  border-inline: var(--rule);\n  border-bottom: var(--rule);\n  background: var(--color-surface-body);\n}\n\n\/* Mixed in srgb, not oklch. Mixing a warm near-white with transparent in oklch\n   leaves the hue channel powerless \u2014 the computed value came out as\n   `oklch(0.9855 0.0057 none \/ 0.92)`, and a residual chroma on an undefined hue\n   renders pink, so the bar read visibly cooler and pinker than the page it sits\n   on. srgb has no hue channel to lose and holds the cream. *\/\n@supports (backdrop-filter: blur(2px)) {\n  .site-header {\n    background: color-mix(in srgb, var(--color-surface-body), transparent 8%);\n    backdrop-filter: blur(8px);\n  }\n}\n\n\/* The bar keeps the tighter button. The component's block padding is sized for\n   a button sitting in a page, which would make the header noticeably taller. *\/\n.site-header .btn {\n  padding-block: var(--space-sm);\n}\n\n.site-header__brand {\n  display: flex;\n  align-items: center;\n  gap: var(--space-xs);\n  margin-right: auto;\n  text-decoration: none;\n  color: var(--color-ink);\n}\n\n.site-header__mark {\n  display: grid;\n  place-items: center;\n  inline-size: var(--space-xl);\n  block-size: var(--space-xl);\n  background: var(--color-action);\n  color: var(--color-on-action);\n  font-family: var(--font-title);\n  font-size: var(--text-sm-size);\n  font-weight: var(--weight-bold);\n}\n\n.site-header__word {\n  font-family: var(--font-title);\n  font-size: var(--title-xs-size);\n  font-weight: var(--weight-bold);\n  letter-spacing: var(--tracking-tight);\n}\n\n\/* Below the md breakpoint the links become a disclosure panel dropped from the\n   bar. The panel is absolute against the sticky bar, which already establishes\n   the containing block \u2014 making the bar static drops it to the top of the page. *\/\n.site-header__nav {\n  display: none;\n  position: absolute;\n  inset-inline: 0;\n  top: 100%;\n  background: var(--color-surface-body);\n  border-bottom: var(--rule);\n}\n\n\/* The panel can outrun a short viewport once the links are set this large, so\n   it caps at the space under the bar and scrolls inside itself. *\/\n.site-header__nav.is-open {\n  display: block;\n  max-block-size: calc(100dvh - var(--space-4xl));\n  overflow-y: auto;\n}\n\n\/* primary_nav emits its own ul\/li\/a, so style by descent, never by adding\n   classes inside the placeholder. The :not([class]) guard keeps this off any\n   classed link \u2014 a bare `.x a { color }` is 0,1,1 and outranks .btn at 0,1,0. *\/\n.site-header__nav ul {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n  display: flex;\n  flex-direction: column;\n}\n\n\/* On the panel the links are the page, not a strip of labels, so they take the\n   heading face at the h3 step. Above md they drop back to reading size in the\n   bar. Navigation is reading text either way \u2014 the mono face is reserved for\n   metadata: dates, counts, field labels, the terminal caption. *\/\n.site-header__nav li a:not([class]) {\n  display: block;\n  padding: var(--space-md) var(--pad);\n  border-top: var(--rule);\n  font-family: var(--font-title);\n  font-size: var(--title-md-size);\n  line-height: var(--title-md-leading);\n  letter-spacing: var(--title-md-tracking);\n  font-weight: var(--weight-semibold);\n  color: var(--color-ink);\n  text-decoration: none;\n}\n\n\/* The call to action lives at the foot of the panel on a small screen, where\n   the bar has no room for it, and full width so it reads as the end of the\n   list rather than a stray control. *\/\n.site-header__navcta {\n  padding: var(--space-lg) var(--pad) var(--space-xl);\n  border-top: var(--rule);\n}\n\n.site-header__navcta .btn {\n  inline-size: 100%;\n}\n\n\/* The bar's own copy of the button is for md and up; below that the panel\n   carries it and two would be one too many. *\/\n.site-header__cta {\n  display: none;\n}\n\n.site-header__nav li:first-child a:not([class]) {\n  border-top: 0;\n}\n\n.site-header__nav li a:not([class]):hover,\n.site-header__nav li a:not([class])[aria-current] {\n  color: var(--color-action);\n}\n\n.site-header__toggle {\n  appearance: none;\n  background: none;\n  border: 0;\n  padding: var(--space-2xs);\n  display: grid;\n  place-items: center;\n  cursor: pointer;\n  color: var(--color-ink);\n  font-family: var(--font-mono);\n  font-size: var(--mono-sm);\n}\n\n.site-header__toggle:hover {\n  color: var(--color-action);\n}\n\n.site-header__toggle .is-close { display: none; }\n.site-header__toggle[aria-expanded=\"true\"] .is-open { display: none; }\n.site-header__toggle[aria-expanded=\"true\"] .is-close { display: inline; }\n\n@media (min-width: 64rem) {\n  .site-header__toggle {\n    display: none;\n  }\n\n  .site-header__cta {\n    display: inline-flex;\n  }\n\n  .site-header__navcta {\n    display: none;\n  }\n\n  .site-header__nav {\n    display: block;\n    position: static;\n    border-bottom: 0;\n    background: none;\n  }\n\n  .site-header__nav ul {\n    flex-direction: row;\n    gap: var(--space-lg);\n  }\n\n  .site-header__nav li a:not([class]) {\n    padding: 0;\n    border-top: 0;\n    font-family: var(--font-text);\n    font-size: var(--text-sm-size);\n    line-height: var(--text-md-leading);\n    letter-spacing: var(--text-sm-tracking);\n    font-weight: var(--weight-regular);\n  }\n}\n\n\/* Contained to the same measure as the page column (.delta-main), with the same side rules, so the\n   page column runs unbroken from the header to the bottom of the footer. *\/\n.site-footer {\n  display: grid;\n  gap: var(--space-xl);\n  inline-size: 100%;\n  max-width: var(--layout-max);\n  margin-inline: auto;\n  padding: var(--space-2xl) var(--pad);\n  border-inline: var(--rule);\n  border-top: var(--rule);\n}\n\n.site-footer__brand {\n  display: grid;\n  gap: var(--space-xs);\n  align-content: start;\n}\n\n.site-footer__word {\n  font-family: var(--font-title);\n  font-size: var(--title-xs-size);\n  font-weight: var(--weight-bold);\n  letter-spacing: var(--tracking-tight);\n  color: var(--color-ink);\n}\n\n.site-footer__line {\n  margin: 0;\n  max-width: var(--line-length-xs);\n  font-size: var(--text-sm-size);\n  line-height: var(--text-sm-leading);\n  color: var(--color-ink-muted);\n}\n\n.site-footer__col {\n  display: grid;\n  gap: var(--space-2xs);\n  align-content: start;\n}\n\n.site-footer__h {\n  margin: 0 0 var(--space-2xs);\n  font-family: var(--font-mono);\n  font-size: var(--mono-sm);\n  text-transform: uppercase;\n  letter-spacing: var(--tracking-wide);\n  color: var(--color-ink);\n}\n\n\/* footer_nav emits its own list markup, so the column styles by descent. The\n   :not([class]) guard keeps this rule off any classed link. *\/\n.site-footer__col ul {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n  display: grid;\n  gap: var(--space-2xs);\n}\n\n\/* Matches the header: navigation is reading text and takes the text face. The\n   mono face stays on the column headings and the fine print, which are labels. *\/\n.site-footer__col li a:not([class]) {\n  font-family: var(--font-text);\n  font-size: var(--text-sm-size);\n  color: var(--color-ink-muted);\n  text-decoration: none;\n}\n\n.site-footer__col li a:not([class]):hover {\n  color: var(--color-action);\n}\n\n.site-footer__fine {\n  margin: 0;\n  padding-top: var(--space-lg);\n  border-top: var(--rule);\n  font-family: var(--font-mono);\n  font-size: var(--mono-sm);\n  color: var(--color-ink-muted);\n}\n\n@media (min-width: 40rem) {\n  .site-footer {\n    grid-template-columns: 2fr 1fr 1fr;\n  }\n\n  .site-footer__fine {\n    grid-column: 1 \/ -1;\n  }\n}"}],"mode":"seed"},"_links":{"self":[{"href":"https:\/\/preview.deltatheme.com\/kiln\/wp-json\/wp\/v2\/pages\/19","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/preview.deltatheme.com\/kiln\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/preview.deltatheme.com\/kiln\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/preview.deltatheme.com\/kiln\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/preview.deltatheme.com\/kiln\/wp-json\/wp\/v2\/comments?post=19"}],"version-history":[{"count":8,"href":"https:\/\/preview.deltatheme.com\/kiln\/wp-json\/wp\/v2\/pages\/19\/revisions"}],"predecessor-version":[{"id":146,"href":"https:\/\/preview.deltatheme.com\/kiln\/wp-json\/wp\/v2\/pages\/19\/revisions\/146"}],"wp:attachment":[{"href":"https:\/\/preview.deltatheme.com\/kiln\/wp-json\/wp\/v2\/media?parent=19"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}